From 4b662ffa6787ac81a62e28e5e23b859cc9ed68a9 Mon Sep 17 00:00:00 2001
From: Florent Le Coz <louiz@louiz.org>
Date: Fri, 6 Sep 2013 14:24:05 +0200
Subject: Much more efficient and clean way to get the last n messages from
 history

---
 src/logger.py | 44 ++++++++++++++++++++++----------------------
 1 file changed, 22 insertions(+), 22 deletions(-)

(limited to 'src')

diff --git a/src/logger.py b/src/logger.py
index f103bcd1..bee6fd79 100644
--- a/src/logger.py
+++ b/src/logger.py
@@ -6,6 +6,7 @@
 # it under the terms of the zlib license. See the COPYING file.
 
 from os import environ, makedirs
+import mmap
 import os
 import re
 from datetime import datetime
@@ -89,7 +90,10 @@ class Logger(object):
 
     def get_logs(self, jid, nb=10):
         """
-        Get the log history for the given jid
+        Get the nb last messages from the log history for the given jid.
+        Note that a message may be more than one line in these files, so
+        this function is a little bit more complicated than “read the last
+        nb lines”.
         """
         if config.get_by_tabname('load_log', 10, jid) <= 0:
             return
@@ -112,31 +116,27 @@ class Logger(object):
         if not fd:
             return
 
-        pos = fd.seek(0, 2)
-        reads = 0
-        check_next = False
-        while reads < nb:
-            if pos == 0:
-                break
-            pos -= 1
-            if pos < 0:
-                pos = 0
-            fd.seek(pos)
-            char = fd.read(1)
-            if char == b'\n':
-                if fd.read(2) in (b'MR', b'MI'):
-                    reads += 1
-                    fd.seek(-2, 1)
-        logs = fd.readlines()
-        fd.close()
-        lines = []
-
-        for line in logs:
-            lines.append(line.decode(errors='replace')[:-1])
+        # read the needed data from the file, we just search nb messages by
+        # searching "\nM" nb times from the end of the file.  We use mmap to
+        # do that efficiently, instead of seek()s and read()s which are costly.
+        with fd:
+            m = mmap.mmap(fd.fileno(), 0, prot=mmap.PROT_READ)
+            pos = m.rfind(b"\nM") # start of messages begin with MI or MR,
+                                  # after a \n
+            # number of message found so far
+            count = 0
+            while pos != -1 and count < nb-1:
+                count += 1
+                pos = m.rfind(b"\nM", 0, pos)
+            if pos == -1:       # If we don't have enough lines in the file
+                pos = 1         # 1, because we do -1 just on the next line
+                                # to get 0 (start of the file)
+            lines = m[pos-1:].decode(errors='replace').split("\n")[:-1]
 
         messages = []
         color = '\x19%s}' % dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
 
+        # now convert that data into actual Message objects
         idx = 0
         while idx < len(lines):
             if lines[idx].startswith(' '): # should not happen ; skip
-- 
cgit v1.2.3