/* This file is part of Konsole, an X terminal. SPDX-FileCopyrightText: 2000 Stephan Kulow Rewritten for QT4 by e_k , Copyright (C)2008 SPDX-License-Identifier: GPL-2.0-or-later 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. */ #include // Own #include "BlockArray.h" // System #include #include #include #include using namespace Konsole; BlockArray::BlockArray() : size(0) , current(size_t(-1)) , index(size_t(-1)) , lastmap(nullptr) , lastmap_index(size_t(-1)) , lastblock(nullptr) , ion(-1) , length(0) { // lastmap_index = index = current = size_t(-1); if (blocksize == 0) { blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize(); } } BlockArray::~BlockArray() { setHistorySize(0); Q_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 nullptr; } size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ; Q_ASSERT(j < size); unmap(); Block *block = (Block *)mmap(nullptr, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize); if (block == (Block *)-1) { perror("mmap"); return nullptr; } lastmap = block; lastmap_index = i; return block; } void BlockArray::unmap() { if (lastmap) { int res = munmap((char *)lastmap, blocksize); if (res < 0) { perror("munmap"); } } lastmap = nullptr; lastmap_index = size_t(-1); } bool BlockArray::setSize(size_t newsize) { return setHistorySize(newsize * 1024 / blocksize); } bool BlockArray::setHistorySize(size_t newsize) { if (size == newsize) { return false; } unmap(); if (!newsize) { delete lastblock; lastblock = nullptr; 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; } Q_ASSERT(!lastblock); lastblock = new Block(); size = newsize; return false; } if (newsize > size) { increaseBuffer(); size = newsize; return false; } else { decreaseBuffer(newsize); ftruncate(ion, length * blocksize); size = newsize; return true; } } void BlockArray::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); }