view gui/src/terminal/BlockArray.cpp @ 13506:c70511cf64ee

Reformatted to GNU Style.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Sun, 17 Jul 2011 22:59:28 +0200
parents 86d6c3b90ad7
children
line wrap: on
line source

/*
    This file is part of Konsole, an X terminal.
    Copyright 2000 by Stephan Kulow <coolo@kde.org>

    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"

// System
#include <assert.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <unistd.h>
#include <stdio.h>

#define KDE_fseek ::fseek
#define KDE_lseek ::lseek

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 = KDE_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)
    {
      //kDebug(1211) << "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 = KDE_fseek (fion, cursor * blocksize, SEEK_SET);
  if (res)
    perror ("fseek");
  res = fread (buffer2, blocksize, 1, fion);
  if (res != 1)
    perror ("fread");

  res = KDE_fseek (fion, newpos * blocksize, SEEK_SET);
  if (res)
    perror ("fseek");
  res = fwrite (buffer2, blocksize, 1, fion);
  if (res != 1)
    perror ("fwrite");
}

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 = KDE_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 = KDE_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);

}