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 TEHISTORY_H
22#define TEHISTORY_H
23
24// Qt
25#include <QBitRef>
26#include <QHash>
27#include <QVector>
28#include <QTemporaryFile>
29
30// KDE
31//#include <ktemporaryfile.h>
32
33// Konsole
34#include "BlockArray.h"
35#include "Character.h"
36
37// map
38#include <sys/mman.h>
39
40namespace Konsole
41{
42
43#if 1
44/*
45 An extendable tmpfile(1) based buffer.
46*/
47
48class HistoryFile
49{
50public:
51 HistoryFile();
52 virtual ~HistoryFile();
53
54 virtual void add(const unsigned char* bytes, int len);
55 virtual void get(unsigned char* bytes, int len, int loc);
56 virtual int len();
57
58 //mmaps the file in read-only mode
59 void map();
60 //un-mmaps the file
61 void unmap();
62 //returns true if the file is mmap'ed
63 bool isMapped() const;
64
65
66private:
67 int ion;
68 int length;
69 QTemporaryFile tmpFile;
70
71 //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed
72 char* fileMap;
73
74 //incremented whenver 'add' is called and decremented whenever
75 //'get' is called.
76 //this is used to detect when a large number of lines are being read and processed from the history
77 //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls).
78 int readWriteBalance;
79
80 //when readWriteBalance goes below this threshold, the file will be mmap'ed automatically
81 static const int MAP_THRESHOLD = -1000;
82};
83#endif
84
85//////////////////////////////////////////////////////////////////////
86
87//////////////////////////////////////////////////////////////////////
88// Abstract base class for file and buffer versions
89//////////////////////////////////////////////////////////////////////
90class HistoryType;
91
92class HistoryScroll
93{
94public:
95 HistoryScroll(HistoryType*);
96 virtual ~HistoryScroll();
97
98 virtual bool hasScroll();
99
100 // access to history
101 virtual int getLines() = 0;
102 virtual int getLineLen(int lineno) = 0;
103 virtual void getCells(int lineno, int colno, int count, Character res[]) = 0;
104 virtual bool isWrappedLine(int lineno) = 0;
105
106 // backward compatibility (obsolete)
107 Character getCell(int lineno, int colno) { Character res; getCells(lineno,colno,1,&res); return res; }
108
109 // adding lines.
110 virtual void addCells(const Character a[], int count) = 0;
111 // convenience method - this is virtual so that subclasses can take advantage
112 // of QVector's implicit copying
113 virtual void addCellsVector(const QVector<Character>& cells)
114 {
115 addCells(cells.data(),cells.size());
116 }
117
118 virtual void addLine(bool previousWrapped=false) = 0;
119
120 //
121 // FIXME: Passing around constant references to HistoryType instances
122 // is very unsafe, because those references will no longer
123 // be valid if the history scroll is deleted.
124 //
125 const HistoryType& getType() { return *m_histType; }
126
127protected:
128 HistoryType* m_histType;
129
130};
131
132#if 1
133
134//////////////////////////////////////////////////////////////////////
135// File-based history (e.g. file log, no limitation in length)
136//////////////////////////////////////////////////////////////////////
137
138class HistoryScrollFile : public HistoryScroll
139{
140public:
141 HistoryScrollFile(const QString &logFileName);
142 virtual ~HistoryScrollFile();
143
144 virtual int getLines();
145 virtual int getLineLen(int lineno);
146 virtual void getCells(int lineno, int colno, int count, Character res[]);
147 virtual bool isWrappedLine(int lineno);
148
149 virtual void addCells(const Character a[], int count);
150 virtual void addLine(bool previousWrapped=false);
151
152private:
153 int startOfLine(int lineno);
154
155 QString m_logFileName;
156 HistoryFile index; // lines Row(int)
157 HistoryFile cells; // text Row(Character)
158 HistoryFile lineflags; // flags Row(unsigned char)
159};
160
161
162//////////////////////////////////////////////////////////////////////
163// Buffer-based history (limited to a fixed nb of lines)
164//////////////////////////////////////////////////////////////////////
165class HistoryScrollBuffer : public HistoryScroll
166{
167public:
168 typedef QVector<Character> HistoryLine;
169
170 HistoryScrollBuffer(unsigned int maxNbLines = 1000);
171 virtual ~HistoryScrollBuffer();
172
173 virtual int getLines();
174 virtual int getLineLen(int lineno);
175 virtual void getCells(int lineno, int colno, int count, Character res[]);
176 virtual bool isWrappedLine(int lineno);
177
178 virtual void addCells(const Character a[], int count);
179 virtual void addCellsVector(const QVector<Character>& cells);
180 virtual void addLine(bool previousWrapped=false);
181
182 void setMaxNbLines(unsigned int nbLines);
183 unsigned int maxNbLines() const { return _maxLineCount; }
184
185
186private:
187 int bufferIndex(int lineNumber);
188
189 HistoryLine* _historyBuffer;
190 QBitArray _wrappedLine;
191 int _maxLineCount;
192 int _usedLines;
193 int _head;
194
195 //QVector<histline*> m_histBuffer;
196 //QBitArray m_wrappedLine;
197 //unsigned int m_maxNbLines;
198 //unsigned int m_nbLines;
199 //unsigned int m_arrayIndex;
200 //bool m_buffFilled;
201};
202
203/*class HistoryScrollBufferV2 : public HistoryScroll
204{
205public:
206 virtual int getLines();
207 virtual int getLineLen(int lineno);
208 virtual void getCells(int lineno, int colno, int count, Character res[]);
209 virtual bool isWrappedLine(int lineno);
210
211 virtual void addCells(const Character a[], int count);
212 virtual void addCells(const QVector<Character>& cells);
213 virtual void addLine(bool previousWrapped=false);
214
215};*/
216
217#endif
218
219//////////////////////////////////////////////////////////////////////
220// Nothing-based history (no history :-)
221//////////////////////////////////////////////////////////////////////
222class HistoryScrollNone : public HistoryScroll
223{
224public:
225 HistoryScrollNone();
226 virtual ~HistoryScrollNone();
227
228 virtual bool hasScroll();
229
230 virtual int getLines();
231 virtual int getLineLen(int lineno);
232 virtual void getCells(int lineno, int colno, int count, Character res[]);
233 virtual bool isWrappedLine(int lineno);
234
235 virtual void addCells(const Character a[], int count);
236 virtual void addLine(bool previousWrapped=false);
237};
238
239//////////////////////////////////////////////////////////////////////
240// BlockArray-based history
241//////////////////////////////////////////////////////////////////////
242class HistoryScrollBlockArray : public HistoryScroll
243{
244public:
245 HistoryScrollBlockArray(size_t size);
246 virtual ~HistoryScrollBlockArray();
247
248 virtual int getLines();
249 virtual int getLineLen(int lineno);
250 virtual void getCells(int lineno, int colno, int count, Character res[]);
251 virtual bool isWrappedLine(int lineno);
252
253 virtual void addCells(const Character a[], int count);
254 virtual void addLine(bool previousWrapped=false);
255
256protected:
257 BlockArray m_blockArray;
258 QHash<int,size_t> m_lineLengths;
259};
260
261//////////////////////////////////////////////////////////////////////
262// History using compact storage
263// This implementation uses a list of fixed-sized blocks
264// where history lines are allocated in (avoids heap fragmentation)
265//////////////////////////////////////////////////////////////////////
266typedef QVector<Character> TextLine;
267
268class CharacterFormat
269{
270public:
271 bool equalsFormat(const CharacterFormat &other) const {
272 return other.rendition==rendition && other.fgColor==fgColor && other.bgColor==bgColor;
273 }
274
275 bool equalsFormat(const Character &c) const {
276 return c.rendition==rendition && c.foregroundColor==fgColor && c.backgroundColor==bgColor;
277 }
278
279 void setFormat(const Character& c) {
280 rendition=c.rendition;
281 fgColor=c.foregroundColor;
282 bgColor=c.backgroundColor;
283 }
284
285 CharacterColor fgColor, bgColor;
286 quint16 startPos;
287 quint8 rendition;
288};
289
290class CompactHistoryBlock
291{
292public:
293
294 CompactHistoryBlock(){
295 blockLength = 4096*64; // 256kb
296 head = (quint8*) mmap(0, blockLength, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
297 //head = (quint8*) malloc(blockLength);
298 Q_ASSERT(head != MAP_FAILED);
299 tail = blockStart = head;
300 allocCount=0;
301 }
302
303 virtual ~CompactHistoryBlock(){
304 //free(blockStart);
305 munmap(blockStart, blockLength);
306 }
307
308 virtual unsigned int remaining(){ return blockStart+blockLength-tail;}
309 virtual unsigned length() { return blockLength; }
310 virtual void* allocate(size_t length);
311 virtual bool contains(void *addr) {return addr>=blockStart && addr<(blockStart+blockLength);}
312 virtual void deallocate();
313 virtual bool isInUse(){ return allocCount!=0; } ;
314
315private:
316 size_t blockLength;
317 quint8* head;
318 quint8* tail;
319 quint8* blockStart;
320 int allocCount;
321};
322
323class CompactHistoryBlockList {
324public:
325 CompactHistoryBlockList() {};
326 ~CompactHistoryBlockList();
327
328 void *allocate( size_t size );
329 void deallocate(void *);
330 int length() {return list.size();}
331private:
332 QList<CompactHistoryBlock*> list;
333};
334
335class CompactHistoryLine
336{
337public:
338 CompactHistoryLine(const TextLine&, CompactHistoryBlockList& blockList);
339 virtual ~CompactHistoryLine();
340
341 // custom new operator to allocate memory from custom pool instead of heap
342 static void *operator new( size_t size, CompactHistoryBlockList& blockList);
343 static void operator delete( void *) { /* do nothing, deallocation from pool is done in destructor*/ } ;
344
345 virtual void getCharacters(Character* array, int length, int startColumn) ;
346 virtual void getCharacter(int index, Character &r) ;
347 virtual bool isWrapped() const {return wrapped;};
348 virtual void setWrapped(bool isWrapped) { wrapped=isWrapped;};
349 virtual unsigned int getLength() const {return length;};
350
351protected:
352 CompactHistoryBlockList& blockList;
353 CharacterFormat* formatArray;
354 quint16 length;
355 quint16* text;
356 quint16 formatLength;
357 bool wrapped;
358};
359
360class CompactHistoryScroll : public HistoryScroll
361{
362 typedef QList<CompactHistoryLine*> HistoryArray;
363
364public:
365 CompactHistoryScroll(unsigned int maxNbLines = 1000);
366 virtual ~CompactHistoryScroll();
367
368 virtual int getLines();
369 virtual int getLineLen(int lineno);
370 virtual void getCells(int lineno, int colno, int count, Character res[]);
371 virtual bool isWrappedLine(int lineno);
372
373 virtual void addCells(const Character a[], int count);
374 virtual void addCellsVector(const TextLine& cells);
375 virtual void addLine(bool previousWrapped=false);
376
377 void setMaxNbLines(unsigned int nbLines);
378 unsigned int maxNbLines() const { return _maxLineCount; }
379
380private:
381 bool hasDifferentColors(const TextLine& line) const;
382 HistoryArray lines;
383 CompactHistoryBlockList blockList;
384
385 unsigned int _maxLineCount;
386};
387
388//////////////////////////////////////////////////////////////////////
389// History type
390//////////////////////////////////////////////////////////////////////
391
392class HistoryType
393{
394public:
395 HistoryType();
396 virtual ~HistoryType();
397
398 /**
399 * Returns true if the history is enabled ( can store lines of output )
400 * or false otherwise.
401 */
402 virtual bool isEnabled() const = 0;
403 /**
404 * Returns true if the history size is unlimited.
405 */
406 bool isUnlimited() const { return maximumLineCount() == 0; }
407 /**
408 * Returns the maximum number of lines which this history type
409 * can store or 0 if the history can store an unlimited number of lines.
410 */
411 virtual int maximumLineCount() const = 0;
412
413 virtual HistoryScroll* scroll(HistoryScroll *) const = 0;
414};
415
416class HistoryTypeNone : public HistoryType
417{
418public:
419 HistoryTypeNone();
420
421 virtual bool isEnabled() const;
422 virtual int maximumLineCount() const;
423
424 virtual HistoryScroll* scroll(HistoryScroll *) const;
425};
426
427class HistoryTypeBlockArray : public HistoryType
428{
429public:
430 HistoryTypeBlockArray(size_t size);
431
432 virtual bool isEnabled() const;
433 virtual int maximumLineCount() const;
434
435 virtual HistoryScroll* scroll(HistoryScroll *) const;
436
437protected:
438 size_t m_size;
439};
440
441#if 1
442class HistoryTypeFile : public HistoryType
443{
444public:
445 HistoryTypeFile(const QString& fileName=QString());
446
447 virtual bool isEnabled() const;
448 virtual const QString& getFileName() const;
449 virtual int maximumLineCount() const;
450
451 virtual HistoryScroll* scroll(HistoryScroll *) const;
452
453protected:
454 QString m_fileName;
455};
456
457
458class HistoryTypeBuffer : public HistoryType
459{
460 friend class HistoryScrollBuffer;
461
462public:
463 HistoryTypeBuffer(unsigned int nbLines);
464
465 virtual bool isEnabled() const;
466 virtual int maximumLineCount() const;
467
468 virtual HistoryScroll* scroll(HistoryScroll *) const;
469
470protected:
471 unsigned int m_nbLines;
472};
473
474class CompactHistoryType : public HistoryType
475{
476public:
477 CompactHistoryType(unsigned int size);
478
479 virtual bool isEnabled() const;
480 virtual int maximumLineCount() const;
481
482 virtual HistoryScroll* scroll(HistoryScroll *) const;
483
484protected:
485 unsigned int m_nbLines;
486};
487
488
489#endif
490
491}
492
493#endif // TEHISTORY_H
494