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 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301 USA.
19*/
20
21#ifndef HISTORY_H
22#define HISTORY_H
23
24// System
25#include <sys/mman.h>
26
27// Qt
28#include <QtCore/QList>
29#include <QtCore/QVector>
30#include <QtCore/QTemporaryFile>
31
32#include "konsole_export.h"
33
34// Konsole
35#include "Character.h"
36
37namespace Konsole
38{
39/*
40 An extendable tmpfile(1) based buffer.
41*/
42
43class HistoryFile
44{
45public:
46 HistoryFile();
47 virtual ~HistoryFile();
48
49 virtual void add(const unsigned char* bytes, int len);
50 virtual void get(unsigned char* bytes, int len, int loc);
51 virtual int len() const;
52
53 //mmaps the file in read-only mode
54 void map();
55 //un-mmaps the file
56 void unmap();
57 //returns true if the file is mmap'ed
58 bool isMapped() const;
59
60
61private:
62 int _fd;
63 int _length;
64 QTemporaryFile _tmpFile;
65
66 //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed
67 char* _fileMap;
68
69 //incremented whenever 'add' is called and decremented whenever
70 //'get' is called.
71 //this is used to detect when a large number of lines are being read and processed from the history
72 //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls).
73 int _readWriteBalance;
74
75 //when _readWriteBalance goes below this threshold, the file will be mmap'ed automatically
76 static const int MAP_THRESHOLD = -1000;
77};
78
79//////////////////////////////////////////////////////////////////////
80
81//////////////////////////////////////////////////////////////////////
82// Abstract base class for file and buffer versions
83//////////////////////////////////////////////////////////////////////
84class HistoryType;
85
86class HistoryScroll
87{
88public:
89 explicit HistoryScroll(HistoryType*);
90 virtual ~HistoryScroll();
91
92 virtual bool hasScroll();
93
94 // access to history
95 virtual int getLines() = 0;
96 virtual int getLineLen(int lineno) = 0;
97 virtual void getCells(int lineno, int colno, int count, Character res[]) = 0;
98 virtual bool isWrappedLine(int lineno) = 0;
99
100 // adding lines.
101 virtual void addCells(const Character a[], int count) = 0;
102 // convenience method - this is virtual so that subclasses can take advantage
103 // of QVector's implicit copying
104 virtual void addCellsVector(const QVector<Character>& cells) {
105 addCells(cells.data(), cells.size());
106 }
107
108 virtual void addLine(bool previousWrapped = false) = 0;
109
110 //
111 // FIXME: Passing around constant references to HistoryType instances
112 // is very unsafe, because those references will no longer
113 // be valid if the history scroll is deleted.
114 //
115 const HistoryType& getType() const {
116 return *_historyType;
117 }
118
119protected:
120 HistoryType* _historyType;
121};
122
123//////////////////////////////////////////////////////////////////////
124// File-based history (e.g. file log, no limitation in length)
125//////////////////////////////////////////////////////////////////////
126
127class KONSOLEPRIVATE_EXPORT HistoryScrollFile : public HistoryScroll
128{
129public:
130 explicit HistoryScrollFile(const QString& logFileName);
131 virtual ~HistoryScrollFile();
132
133 virtual int getLines();
134 virtual int getLineLen(int lineno);
135 virtual void getCells(int lineno, int colno, int count, Character res[]);
136 virtual bool isWrappedLine(int lineno);
137
138 virtual void addCells(const Character a[], int count);
139 virtual void addLine(bool previousWrapped = false);
140
141private:
142 int startOfLine(int lineno);
143
144 HistoryFile _index; // lines Row(int)
145 HistoryFile _cells; // text Row(Character)
146 HistoryFile _lineflags; // flags Row(unsigned char)
147};
148
149//////////////////////////////////////////////////////////////////////
150// Nothing-based history (no history :-)
151//////////////////////////////////////////////////////////////////////
152class KONSOLEPRIVATE_EXPORT HistoryScrollNone : public HistoryScroll
153{
154public:
155 HistoryScrollNone();
156 virtual ~HistoryScrollNone();
157
158 virtual bool hasScroll();
159
160 virtual int getLines();
161 virtual int getLineLen(int lineno);
162 virtual void getCells(int lineno, int colno, int count, Character res[]);
163 virtual bool isWrappedLine(int lineno);
164
165 virtual void addCells(const Character a[], int count);
166 virtual void addLine(bool previousWrapped = false);
167};
168
169//////////////////////////////////////////////////////////////////////
170// History using compact storage
171// This implementation uses a list of fixed-sized blocks
172// where history lines are allocated in (avoids heap fragmentation)
173//////////////////////////////////////////////////////////////////////
174typedef QVector<Character> TextLine;
175
176class CharacterFormat
177{
178public:
179 bool equalsFormat(const CharacterFormat& other) const {
180 return (other.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && other.fgColor == fgColor && other.bgColor == bgColor;
181 }
182
183 bool equalsFormat(const Character& c) const {
184 return (c.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && c.foregroundColor == fgColor && c.backgroundColor == bgColor;
185 }
186
187 void setFormat(const Character& c) {
188 rendition = c.rendition;
189 fgColor = c.foregroundColor;
190 bgColor = c.backgroundColor;
191 isRealCharacter = c.isRealCharacter;
192 }
193
194 CharacterColor fgColor, bgColor;
195 quint16 startPos;
196 quint8 rendition;
197 bool isRealCharacter;
198};
199
200class CompactHistoryBlock
201{
202public:
203 CompactHistoryBlock() {
204 _blockLength = 4096 * 64; // 256kb
205 _head = (quint8*) mmap(0, _blockLength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
206 //_head = (quint8*) malloc(_blockLength);
207 Q_ASSERT(_head != MAP_FAILED);
208 _tail = _blockStart = _head;
209 _allocCount = 0;
210 }
211
212 virtual ~CompactHistoryBlock() {
213 //free(_blockStart);
214 munmap(_blockStart, _blockLength);
215 }
216
217 virtual unsigned int remaining() {
218 return _blockStart + _blockLength - _tail;
219 }
220 virtual unsigned length() {
221 return _blockLength;
222 }
223 virtual void* allocate(size_t length);
224 virtual bool contains(void* addr) {
225 return addr >= _blockStart && addr < (_blockStart + _blockLength);
226 }
227 virtual void deallocate();
228 virtual bool isInUse() {
229 return _allocCount != 0;
230 };
231
232private:
233 size_t _blockLength;
234 quint8* _head;
235 quint8* _tail;
236 quint8* _blockStart;
237 int _allocCount;
238};
239
240class CompactHistoryBlockList
241{
242public:
243 CompactHistoryBlockList() {}
244 ~CompactHistoryBlockList();
245
246 void* allocate(size_t size);
247 void deallocate(void *);
248 int length() {
249 return list.size();
250 }
251private:
252 QList<CompactHistoryBlock*> list;
253};
254
255class CompactHistoryLine
256{
257public:
258 CompactHistoryLine(const TextLine&, CompactHistoryBlockList& blockList);
259 virtual ~CompactHistoryLine();
260
261 // custom new operator to allocate memory from custom pool instead of heap
262 static void* operator new(size_t size, CompactHistoryBlockList& blockList);
263 static void operator delete(void *) {
264 /* do nothing, deallocation from pool is done in destructor*/
265 };
266
267 virtual void getCharacters(Character* array, int length, int startColumn);
268 virtual void getCharacter(int index, Character& r);
269 virtual bool isWrapped() const {
270 return _wrapped;
271 };
272 virtual void setWrapped(bool value) {
273 _wrapped = value;
274 };
275 virtual unsigned int getLength() const {
276 return _length;
277 };
278
279protected:
280 CompactHistoryBlockList& _blockListRef;
281 CharacterFormat* _formatArray;
282 quint16 _length;
283 quint16* _text;
284 quint16 _formatLength;
285 bool _wrapped;
286};
287
288class KONSOLEPRIVATE_EXPORT CompactHistoryScroll : public HistoryScroll
289{
290 typedef QList<CompactHistoryLine*> HistoryArray;
291
292public:
293 explicit CompactHistoryScroll(unsigned int maxNbLines = 1000);
294 virtual ~CompactHistoryScroll();
295
296 virtual int getLines();
297 virtual int getLineLen(int lineno);
298 virtual void getCells(int lineno, int colno, int count, Character res[]);
299 virtual bool isWrappedLine(int lineno);
300
301 virtual void addCells(const Character a[], int count);
302 virtual void addCellsVector(const TextLine& cells);
303 virtual void addLine(bool previousWrapped = false);
304
305 void setMaxNbLines(unsigned int nbLines);
306
307private:
308 bool hasDifferentColors(const TextLine& line) const;
309 HistoryArray _lines;
310 CompactHistoryBlockList _blockList;
311
312 unsigned int _maxLineCount;
313};
314
315//////////////////////////////////////////////////////////////////////
316// History type
317//////////////////////////////////////////////////////////////////////
318
319class KONSOLEPRIVATE_EXPORT HistoryType
320{
321public:
322 HistoryType();
323 virtual ~HistoryType();
324
325 /**
326 * Returns true if the history is enabled ( can store lines of output )
327 * or false otherwise.
328 */
329 virtual bool isEnabled() const = 0;
330 /**
331 * Returns the maximum number of lines which this history type
332 * can store or -1 if the history can store an unlimited number of lines.
333 */
334 virtual int maximumLineCount() const = 0;
335 /**
336 * Converts from one type of HistoryScroll to another or if given the
337 * same type, returns it.
338 */
339 virtual HistoryScroll* scroll(HistoryScroll *) const = 0;
340 /**
341 * Returns true if the history size is unlimited.
342 */
343 bool isUnlimited() const {
344 return maximumLineCount() == -1;
345 }
346};
347
348class KONSOLEPRIVATE_EXPORT HistoryTypeNone : public HistoryType
349{
350public:
351 HistoryTypeNone();
352
353 virtual bool isEnabled() const;
354 virtual int maximumLineCount() const;
355
356 virtual HistoryScroll* scroll(HistoryScroll *) const;
357};
358
359class KONSOLEPRIVATE_EXPORT HistoryTypeFile : public HistoryType
360{
361public:
362 explicit HistoryTypeFile(const QString& fileName = QString());
363
364 virtual bool isEnabled() const;
365 virtual int maximumLineCount() const;
366
367 virtual HistoryScroll* scroll(HistoryScroll *) const;
368
369protected:
370 QString _fileName;
371};
372
373class KONSOLEPRIVATE_EXPORT CompactHistoryType : public HistoryType
374{
375public:
376 explicit CompactHistoryType(unsigned int size);
377
378 virtual bool isEnabled() const;
379 virtual int maximumLineCount() const;
380
381 virtual HistoryScroll* scroll(HistoryScroll *) const;
382
383protected:
384 unsigned int _maxLines;
385};
386}
387
388#endif // HISTORY_H
389