History.h 10.6 KB
Newer Older
1
2
/*
    This file is part of Konsole, an X terminal.
3
    Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

    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.
*/
20

21
22
#ifndef HISTORY_H
#define HISTORY_H
23

Jekyll Wu's avatar
Jekyll Wu committed
24
25
26
// System
#include <sys/mman.h>

27
// Qt
28
#include <QtCore/QList>
Dirk Mueller's avatar
Dirk Mueller committed
29
#include <QtCore/QVector>
30
#include <QtCore/QTemporaryFile>
31

32
// Konsole
33
#include "Character.h"
34

35
36
namespace Konsole
{
37
38
39
40
41
42
43
/*
   An extendable tmpfile(1) based buffer.
*/

class HistoryFile
{
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
44
45
    HistoryFile();
    virtual ~HistoryFile();
46

Kurt Hindenburg's avatar
Kurt Hindenburg committed
47
48
49
    virtual void add(const unsigned char* bytes, int len);
    virtual void get(unsigned char* bytes, int len, int loc);
    virtual int  len() const;
50

Kurt Hindenburg's avatar
Kurt Hindenburg committed
51
52
53
54
55
56
    //mmaps the file in read-only mode
    void map();
    //un-mmaps the file
    void unmap();
    //returns true if the file is mmap'ed
    bool isMapped() const;
57
58


59
private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
60
61
    int  _fd;
    int  _length;
62
    QTemporaryFile _tmpFile;
63

Kurt Hindenburg's avatar
Kurt Hindenburg committed
64
65
    //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed
    char* _fileMap;
66

Kurt Hindenburg's avatar
Kurt Hindenburg committed
67
68
69
70
71
    //incremented whenever 'add' is called and decremented whenever
    //'get' is called.
    //this is used to detect when a large number of lines are being read and processed from the history
    //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls).
    int _readWriteBalance;
72

Kurt Hindenburg's avatar
Kurt Hindenburg committed
73
74
    //when _readWriteBalance goes below this threshold, the file will be mmap'ed automatically
    static const int MAP_THRESHOLD = -1000;
75
76
77
78
79
80
81
82
83
84
85
86
};

//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Abstract base class for file and buffer versions
//////////////////////////////////////////////////////////////////////
class HistoryType;

class HistoryScroll
{
public:
87
    explicit HistoryScroll(HistoryType*);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    virtual ~HistoryScroll();

    virtual bool hasScroll();

    // access to history
    virtual int  getLines() = 0;
    virtual int  getLineLen(int lineno) = 0;
    virtual void getCells(int lineno, int colno, int count, Character res[]) = 0;
    virtual bool isWrappedLine(int lineno) = 0;

    // adding lines.
    virtual void addCells(const Character a[], int count) = 0;
    // convenience method - this is virtual so that subclasses can take advantage
    // of QVector's implicit copying
    virtual void addCellsVector(const QVector<Character>& cells) {
        addCells(cells.data(), cells.size());
    }

    virtual void addLine(bool previousWrapped = false) = 0;

    //
    // FIXME:  Passing around constant references to HistoryType instances
    // is very unsafe, because those references will no longer
    // be valid if the history scroll is deleted.
    //
Kurt Hindenburg's avatar
Kurt Hindenburg committed
113
    const HistoryType& getType() const {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
114
115
        return *_historyType;
    }
116
117

protected:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
118
    HistoryType* _historyType;
119
120
121
122
123
124
125
126
127
};

//////////////////////////////////////////////////////////////////////
// File-based history (e.g. file log, no limitation in length)
//////////////////////////////////////////////////////////////////////

class HistoryScrollFile : public HistoryScroll
{
public:
128
    explicit HistoryScrollFile(const QString& logFileName);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
129
    virtual ~HistoryScrollFile();
130

Kurt Hindenburg's avatar
Kurt Hindenburg committed
131
132
133
134
    virtual int  getLines();
    virtual int  getLineLen(int lineno);
    virtual void getCells(int lineno, int colno, int count, Character res[]);
    virtual bool isWrappedLine(int lineno);
135

Kurt Hindenburg's avatar
Kurt Hindenburg committed
136
137
    virtual void addCells(const Character a[], int count);
    virtual void addLine(bool previousWrapped = false);
138
139

private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
140
    int startOfLine(int lineno);
141

142
143
144
    HistoryFile _index; // lines Row(int)
    HistoryFile _cells; // text  Row(Character)
    HistoryFile _lineflags; // flags Row(unsigned char)
145
146
147
148
149
150
151
152
};

//////////////////////////////////////////////////////////////////////
// Nothing-based history (no history :-)
//////////////////////////////////////////////////////////////////////
class HistoryScrollNone : public HistoryScroll
{
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
153
154
    HistoryScrollNone();
    virtual ~HistoryScrollNone();
155

Kurt Hindenburg's avatar
Kurt Hindenburg committed
156
    virtual bool hasScroll();
157

Kurt Hindenburg's avatar
Kurt Hindenburg committed
158
159
160
161
    virtual int  getLines();
    virtual int  getLineLen(int lineno);
    virtual void getCells(int lineno, int colno, int count, Character res[]);
    virtual bool isWrappedLine(int lineno);
162

Kurt Hindenburg's avatar
Kurt Hindenburg committed
163
164
    virtual void addCells(const Character a[], int count);
    virtual void addLine(bool previousWrapped = false);
165
166
};

167
168
169
170
171
172
173
174
175
176
//////////////////////////////////////////////////////////////////////
// History using compact storage
// This implementation uses a list of fixed-sized blocks
// where history lines are allocated in (avoids heap fragmentation)
//////////////////////////////////////////////////////////////////////
typedef QVector<Character> TextLine;

class CharacterFormat
{
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
177
178
179
180
181
182
183
184
185
186
187
188
    bool equalsFormat(const CharacterFormat& other) const {
        return (other.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && other.fgColor == fgColor && other.bgColor == bgColor;
    }

    bool equalsFormat(const Character& c) const {
        return (c.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && c.foregroundColor == fgColor && c.backgroundColor == bgColor;
    }

    void setFormat(const Character& c) {
        rendition = c.rendition;
        fgColor = c.foregroundColor;
        bgColor = c.backgroundColor;
189
        isRealCharacter = c.isRealCharacter;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
190
191
192
193
194
    }

    CharacterColor fgColor, bgColor;
    quint16 startPos;
    quint8 rendition;
195
    bool isRealCharacter;
196
197
198
199
200
};

class CompactHistoryBlock
{
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
201
    CompactHistoryBlock() {
202
203
204
205
206
207
        _blockLength = 4096 * 64; // 256kb
        _head = (quint8*) mmap(0, _blockLength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
        //_head = (quint8*) malloc(_blockLength);
        Q_ASSERT(_head != MAP_FAILED);
        _tail = _blockStart = _head;
        _allocCount = 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
208
209
210
    }

    virtual ~CompactHistoryBlock() {
211
212
        //free(_blockStart);
        munmap(_blockStart, _blockLength);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
213
214
215
    }

    virtual unsigned int remaining() {
216
        return _blockStart + _blockLength - _tail;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
217
218
    }
    virtual unsigned  length() {
219
        return _blockLength;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
220
221
222
    }
    virtual void* allocate(size_t length);
    virtual bool contains(void* addr) {
223
        return addr >= _blockStart && addr < (_blockStart + _blockLength);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
224
225
226
    }
    virtual void deallocate();
    virtual bool isInUse() {
227
        return _allocCount != 0;
228
    };
229
230

private:
231
232
233
234
235
    size_t _blockLength;
    quint8* _head;
    quint8* _tail;
    quint8* _blockStart;
    int _allocCount;
236
237
};

Kurt Hindenburg's avatar
Kurt Hindenburg committed
238
239
class CompactHistoryBlockList
{
240
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
241
242
243
244
245
246
247
248
    CompactHistoryBlockList() {};
    ~CompactHistoryBlockList();

    void* allocate(size_t size);
    void deallocate(void *);
    int length() {
        return list.size();
    }
249
private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
250
    QList<CompactHistoryBlock*> list;
251
252
253
254
255
};

class CompactHistoryLine
{
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
256
257
258
259
260
261
262
    CompactHistoryLine(const TextLine&, CompactHistoryBlockList& blockList);
    virtual ~CompactHistoryLine();

    // custom new operator to allocate memory from custom pool instead of heap
    static void* operator new(size_t size, CompactHistoryBlockList& blockList);
    static void operator delete(void *) {
        /* do nothing, deallocation from pool is done in destructor*/
263
    };
Kurt Hindenburg's avatar
Kurt Hindenburg committed
264

265
266
    virtual void getCharacters(Character* array, int length, int startColumn);
    virtual void getCharacter(int index, Character& r);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
267
    virtual bool isWrapped() const {
268
        return _wrapped;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
269
    };
270
    virtual void setWrapped(bool value) {
271
        _wrapped = value;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
272
273
    };
    virtual unsigned int getLength() const {
274
        return _length;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
275
    };
276
277

protected:
278
279
280
281
282
283
    CompactHistoryBlockList& _blockListRef;
    CharacterFormat* _formatArray;
    quint16 _length;
    quint16* _text;
    quint16 _formatLength;
    bool _wrapped;
284
285
286
287
};

class CompactHistoryScroll : public HistoryScroll
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
288
    typedef QList<CompactHistoryLine*> HistoryArray;
289
290

public:
291
    explicit CompactHistoryScroll(unsigned int maxNbLines = 1000);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
292
    virtual ~CompactHistoryScroll();
293

Kurt Hindenburg's avatar
Kurt Hindenburg committed
294
295
296
297
    virtual int  getLines();
    virtual int  getLineLen(int lineno);
    virtual void getCells(int lineno, int colno, int count, Character res[]);
    virtual bool isWrappedLine(int lineno);
298

Kurt Hindenburg's avatar
Kurt Hindenburg committed
299
300
301
    virtual void addCells(const Character a[], int count);
    virtual void addCellsVector(const TextLine& cells);
    virtual void addLine(bool previousWrapped = false);
302

Kurt Hindenburg's avatar
Kurt Hindenburg committed
303
    void setMaxNbLines(unsigned int nbLines);
304
305

private:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
306
    bool hasDifferentColors(const TextLine& line) const;
307
308
    HistoryArray _lines;
    CompactHistoryBlockList _blockList;
309

Kurt Hindenburg's avatar
Kurt Hindenburg committed
310
    unsigned int _maxLineCount;
311
312
};

313
314
315
316
317
318
319
//////////////////////////////////////////////////////////////////////
// History type
//////////////////////////////////////////////////////////////////////

class HistoryType
{
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
    HistoryType();
    virtual ~HistoryType();

    /**
     * Returns true if the history is enabled ( can store lines of output )
     * or false otherwise.
     */
    virtual bool isEnabled() const = 0;
    /**
     * Returns the maximum number of lines which this history type
     * can store or -1 if the history can store an unlimited number of lines.
     */
    virtual int maximumLineCount() const = 0;
    /**
     * TODO: document me
     */
    virtual HistoryScroll* scroll(HistoryScroll *) const = 0;
    /**
     * Returns true if the history size is unlimited.
     */
    bool isUnlimited() const {
        return maximumLineCount() == -1;
    }
343
344
345
346
347
};

class HistoryTypeNone : public HistoryType
{
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
348
    HistoryTypeNone();
349

Kurt Hindenburg's avatar
Kurt Hindenburg committed
350
351
    virtual bool isEnabled() const;
    virtual int maximumLineCount() const;
352

Kurt Hindenburg's avatar
Kurt Hindenburg committed
353
    virtual HistoryScroll* scroll(HistoryScroll *) const;
354
355
356
357
358
};

class HistoryTypeFile : public HistoryType
{
public:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
359
    HistoryTypeFile(const QString& fileName = QString());
360

Kurt Hindenburg's avatar
Kurt Hindenburg committed
361
362
    virtual bool isEnabled() const;
    virtual int maximumLineCount() const;
363

Kurt Hindenburg's avatar
Kurt Hindenburg committed
364
    virtual HistoryScroll* scroll(HistoryScroll *) const;
365
366

protected:
367
    QString _fileName;
368
369
};

370
371
372
class CompactHistoryType : public HistoryType
{
public:
373
    explicit CompactHistoryType(unsigned int size);
374

Kurt Hindenburg's avatar
Kurt Hindenburg committed
375
376
    virtual bool isEnabled() const;
    virtual int maximumLineCount() const;
377

Kurt Hindenburg's avatar
Kurt Hindenburg committed
378
    virtual HistoryScroll* scroll(HistoryScroll *) const;
379
380

protected:
381
    unsigned int _maxLines;
382
};
Stephan Binner's avatar
Stephan Binner committed
383
}
384

385
#endif // HISTORY_H