diff lib/BlockArray.cpp @ 15628:e67d0d06c18b

Forked from QTermWidget.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Mon, 23 Jan 2012 12:22:13 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/BlockArray.cpp	Mon Jan 23 12:22:13 2012 +0100
@@ -0,0 +1,338 @@
+/*
+    This file is part of Konsole, an X terminal.
+    Copyright (C) 2000 by Stephan Kulow <coolo@kde.org>
+
+    Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301  USA.
+
+*/
+
+// Own
+#include "BlockArray.h"
+
+#include <QtCore>
+
+// System
+#include <assert.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <stdio.h>
+
+
+using namespace Konsole;
+
+static int blocksize = 0;
+
+BlockArray::BlockArray()
+    : size(0),
+      current(size_t(-1)),
+      index(size_t(-1)),
+      lastmap(0),
+      lastmap_index(size_t(-1)),
+      lastblock(0), ion(-1),
+      length(0)
+{
+    // lastmap_index = index = current = size_t(-1);
+    if (blocksize == 0)
+        blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize();
+
+}
+
+BlockArray::~BlockArray()
+{
+    setHistorySize(0);
+    assert(!lastblock);
+}
+
+size_t BlockArray::append(Block *block)
+{
+    if (!size)
+        return size_t(-1);
+
+    ++current;
+    if (current >= size) current = 0;
+
+    int rc;
+    rc = lseek(ion, current * blocksize, SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setHistorySize(0); return size_t(-1); }
+    rc = write(ion, block, blocksize); if (rc < 0) { perror("HistoryBuffer::add.write"); setHistorySize(0); return size_t(-1); }
+
+    length++;
+    if (length > size) length = size;
+
+    ++index;
+
+    delete block;
+    return current;
+}
+
+size_t BlockArray::newBlock()
+{
+    if (!size)
+        return size_t(-1);
+    append(lastblock);
+
+    lastblock = new Block();
+    return index + 1;
+}
+
+Block *BlockArray::lastBlock() const
+{
+    return lastblock;
+}
+
+bool BlockArray::has(size_t i) const
+{
+    if (i == index + 1)
+        return true;
+
+    if (i > index)
+        return false;
+    if (index - i >= length)
+        return false;
+    return true;
+}
+
+const Block* BlockArray::at(size_t i)
+{
+    if (i == index + 1)
+        return lastblock;
+
+    if (i == lastmap_index)
+        return lastmap;
+
+    if (i > index) {
+        qDebug() << "BlockArray::at() i > index\n";
+        return 0;
+    }
+    
+//     if (index - i >= length) {
+//         kDebug(1211) << "BlockArray::at() index - i >= length\n";
+//         return 0;
+//     }
+
+    size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ;
+
+    assert(j < size);
+    unmap();
+
+    Block *block = (Block*)mmap(0, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize);
+
+    if (block == (Block*)-1) { perror("mmap"); return 0; }
+
+    lastmap = block;
+    lastmap_index = i;
+
+    return block;
+}
+
+void BlockArray::unmap()
+{
+    if (lastmap) {
+        int res = munmap((char*)lastmap, blocksize);
+        if (res < 0) perror("munmap");
+    }
+    lastmap = 0;
+    lastmap_index = size_t(-1);
+}
+
+bool BlockArray::setSize(size_t newsize)
+{
+    return setHistorySize(newsize * 1024 / blocksize);
+}
+
+bool BlockArray::setHistorySize(size_t newsize)
+{
+//    kDebug(1211) << "setHistorySize " << size << " " << newsize;
+
+    if (size == newsize)
+        return false;
+
+    unmap();
+
+    if (!newsize) {
+        delete lastblock;
+        lastblock = 0;
+        if (ion >= 0) close(ion);
+        ion = -1;
+        current = size_t(-1);
+        return true;
+    }
+
+    if (!size) {
+        FILE* tmp = tmpfile();
+        if (!tmp) {
+            perror("konsole: cannot open temp file.\n");
+        } else {
+            ion = dup(fileno(tmp));
+            if (ion<0) {
+                perror("konsole: cannot dup temp file.\n");
+                fclose(tmp);
+            }
+        }
+        if (ion < 0)
+            return false;
+
+        assert(!lastblock);
+
+        lastblock = new Block();
+        size = newsize;
+        return false;
+    }
+
+    if (newsize > size) {
+        increaseBuffer();
+        size = newsize;
+        return false;
+    } else {
+        decreaseBuffer(newsize);
+        if (ftruncate(ion, length*blocksize) == -1)
+		  perror("ftruncate");
+        size = newsize;
+
+        return true;
+    }
+}
+
+void moveBlock(FILE *fion, int cursor, int newpos, char *buffer2)
+{
+    int res = fseek(fion, cursor * blocksize, SEEK_SET);
+    if (res)
+        perror("fseek");
+    res = fread(buffer2, blocksize, 1, fion);
+    if (res != 1)
+        perror("fread");
+
+    res = fseek(fion, newpos * blocksize, SEEK_SET);
+    if (res)
+        perror("fseek");
+    res = fwrite(buffer2, blocksize, 1, fion);
+    if (res != 1)
+        perror("fwrite");
+    //    printf("moving block %d to %d\n", cursor, newpos);
+}
+
+void BlockArray::decreaseBuffer(size_t newsize)
+{
+    if (index < newsize) // still fits in whole
+        return;
+
+    int offset = (current - (newsize - 1) + size) % size;
+
+    if (!offset)
+        return;
+
+    // The Block constructor could do somthing in future...
+    char *buffer1 = new char[blocksize];
+
+    FILE *fion = fdopen(dup(ion), "w+b");
+    if (!fion) {
+        delete [] buffer1;
+        perror("fdopen/dup");
+        return;
+    }
+
+    int firstblock;
+    if (current <= newsize) {
+        firstblock = current + 1;
+    } else {
+        firstblock = 0;
+    }
+
+    size_t oldpos;
+    for (size_t i = 0, cursor=firstblock; i < newsize; i++) {
+        oldpos = (size + cursor + offset) % size;
+        moveBlock(fion, oldpos, cursor, buffer1);
+        if (oldpos < newsize) {
+            cursor = oldpos;
+        } else
+            cursor++;
+    }
+
+    current = newsize - 1;
+    length = newsize;
+
+    delete [] buffer1;
+
+    fclose(fion);
+
+}
+
+void BlockArray::increaseBuffer()
+{
+    if (index < size) // not even wrapped once
+        return;
+
+    int offset = (current + size + 1) % size;
+    if (!offset) // no moving needed
+        return;
+
+    // The Block constructor could do somthing in future...
+    char *buffer1 = new char[blocksize];
+    char *buffer2 = new char[blocksize];
+
+    int runs = 1;
+    int bpr = size; // blocks per run
+
+    if (size % offset == 0) {
+        bpr = size / offset;
+        runs = offset;
+    }
+
+    FILE *fion = fdopen(dup(ion), "w+b");
+    if (!fion) {
+        perror("fdopen/dup");
+	delete [] buffer1;
+	delete [] buffer2;
+        return;
+    }
+
+    int res;
+    for (int i = 0; i < runs; i++)
+    {
+        // free one block in chain
+        int firstblock = (offset + i) % size;
+        res = fseek(fion, firstblock * blocksize, SEEK_SET);
+        if (res)
+            perror("fseek");
+        res = fread(buffer1, blocksize, 1, fion);
+        if (res != 1)
+            perror("fread");
+        int newpos = 0;
+        for (int j = 1, cursor=firstblock; j < bpr; j++)
+        {
+            cursor = (cursor + offset) % size;
+            newpos = (cursor - offset + size) % size;
+            moveBlock(fion, cursor, newpos, buffer2);
+        }
+        res = fseek(fion, i * blocksize, SEEK_SET);
+        if (res)
+            perror("fseek");
+        res = fwrite(buffer1, blocksize, 1, fion);
+        if (res != 1)
+            perror("fwrite");
+    }
+    current = size - 1;
+    length = size;
+
+    delete [] buffer1;
+    delete [] buffer2;
+
+    fclose(fion);
+
+}
+