File indexing completed on 2025-03-16 08:11:25
0001 /* 0002 This file is part of Konsole, an X terminal. 0003 SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 0007 This program is distributed in the hope that it will be useful, 0008 but WITHOUT ANY WARRANTY; without even the implied warranty of 0009 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0010 GNU General Public License for more details. 0011 0012 You should have received a copy of the GNU General Public License 0013 along with this program; if not, write to the Free Software 0014 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0015 02110-1301 USA. 0016 */ 0017 0018 #ifndef TEHISTORY_H 0019 #define TEHISTORY_H 0020 0021 // Qt 0022 #include <QBitRef> 0023 #include <QHash> 0024 #include <QTemporaryFile> 0025 #include <QVector> 0026 0027 // KDE 0028 // #include <ktemporaryfile.h> 0029 0030 // Konsole 0031 #include "BlockArray.h" 0032 #include "Character.h" 0033 0034 // map 0035 #include <memory> 0036 #include <sys/mman.h> 0037 0038 namespace Konsole 0039 { 0040 0041 #if 1 0042 /* 0043 An extendable tmpfile(1) based buffer. 0044 */ 0045 0046 class HistoryFile 0047 { 0048 public: 0049 HistoryFile(); 0050 virtual ~HistoryFile(); 0051 0052 virtual void add(const unsigned char *bytes, int len); 0053 virtual void get(unsigned char *bytes, int len, int loc); 0054 virtual int len(); 0055 0056 // mmaps the file in read-only mode 0057 void map(); 0058 // un-mmaps the file 0059 void unmap(); 0060 // returns true if the file is mmap'ed 0061 bool isMapped() const; 0062 0063 private: 0064 int ion; 0065 int length; 0066 QTemporaryFile tmpFile; 0067 0068 // pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed 0069 char *fileMap; 0070 0071 // incremented whenver 'add' is called and decremented whenever 0072 //'get' is called. 0073 // this is used to detect when a large number of lines are being read and processed from the history 0074 // and automatically mmap the file for better performance (saves the overhead of many lseek-read calls). 0075 int readWriteBalance; 0076 0077 // when readWriteBalance goes below this threshold, the file will be mmap'ed automatically 0078 static const int MAP_THRESHOLD = -1000; 0079 }; 0080 #endif 0081 0082 ////////////////////////////////////////////////////////////////////// 0083 0084 ////////////////////////////////////////////////////////////////////// 0085 // Abstract base class for file and buffer versions 0086 ////////////////////////////////////////////////////////////////////// 0087 class HistoryType; 0088 0089 class HistoryScroll 0090 { 0091 public: 0092 HistoryScroll(HistoryType *); 0093 virtual ~HistoryScroll(); 0094 0095 virtual bool hasScroll(); 0096 0097 // access to history 0098 virtual int getLines() = 0; 0099 virtual int getLineLen(int lineno) = 0; 0100 virtual void getCells(int lineno, int colno, int count, std::span<Character> res) = 0; 0101 virtual bool isWrappedLine(int lineno) = 0; 0102 0103 // backward compatibility (obsolete) 0104 Character getCell(int lineno, int colno) 0105 { 0106 Character res; 0107 getCells(lineno, colno, 1, std::span(&res, 1)); 0108 return res; 0109 } 0110 0111 // adding lines. 0112 virtual void addCells(std::span<const Character> a, int count) = 0; 0113 // convenience method - this is virtual so that subclasses can take advantage 0114 // of QVector's implicit copying 0115 virtual void addCellsVector(const QVector<Character> &cells) 0116 { 0117 auto span = std::span<const Character>{cells.data(), size_t(cells.size())}; 0118 addCells(span, cells.size()); 0119 } 0120 0121 virtual void addLine(bool previousWrapped = false) = 0; 0122 0123 // 0124 // FIXME: Passing around constant references to HistoryType instances 0125 // is very unsafe, because those references will no longer 0126 // be valid if the history scroll is deleted. 0127 // 0128 const HistoryType &getType() 0129 { 0130 return *m_histType; 0131 } 0132 0133 protected: 0134 HistoryType *m_histType; 0135 }; 0136 0137 #if 1 0138 0139 ////////////////////////////////////////////////////////////////////// 0140 // File-based history (e.g. file log, no limitation in length) 0141 ////////////////////////////////////////////////////////////////////// 0142 0143 class HistoryScrollFile : public HistoryScroll 0144 { 0145 public: 0146 HistoryScrollFile(const QString &logFileName); 0147 ~HistoryScrollFile() override; 0148 0149 int getLines() override; 0150 int getLineLen(int lineno) override; 0151 void getCells(int lineno, int colno, int count, std::span<Character> res) override; 0152 bool isWrappedLine(int lineno) override; 0153 0154 void addCells(std::span<const Character> a, int count) override; 0155 void addLine(bool previousWrapped = false) override; 0156 0157 private: 0158 int startOfLine(int lineno); 0159 0160 QString m_logFileName; 0161 HistoryFile index; // lines Row(int) 0162 HistoryFile cells; // text Row(Character) 0163 HistoryFile lineflags; // flags Row(unsigned char) 0164 }; 0165 0166 ////////////////////////////////////////////////////////////////////// 0167 // Buffer-based history (limited to a fixed nb of lines) 0168 ////////////////////////////////////////////////////////////////////// 0169 class HistoryScrollBuffer : public HistoryScroll 0170 { 0171 public: 0172 typedef QVector<Character> HistoryLine; 0173 0174 HistoryScrollBuffer(unsigned int maxNbLines = 1000); 0175 ~HistoryScrollBuffer() override; 0176 0177 int getLines() override; 0178 int getLineLen(int lineno) override; 0179 void getCells(int lineno, int colno, int count, std::span<Character> res) override; 0180 bool isWrappedLine(int lineno) override; 0181 0182 void addCells(std::span<const Character> a, int count) override; 0183 void addCellsVector(const QVector<Character> &cells) override; 0184 void addLine(bool previousWrapped = false) override; 0185 0186 void setMaxNbLines(unsigned int nbLines); 0187 unsigned int maxNbLines() const 0188 { 0189 return _maxLineCount; 0190 } 0191 0192 private: 0193 int bufferIndex(int lineNumber) const; 0194 0195 HistoryLine *_historyBuffer; 0196 QBitArray _wrappedLine; 0197 int _maxLineCount; 0198 int _usedLines; 0199 int _head; 0200 0201 // QVector<histline*> m_histBuffer; 0202 // QBitArray m_wrappedLine; 0203 // unsigned int m_maxNbLines; 0204 // unsigned int m_nbLines; 0205 // unsigned int m_arrayIndex; 0206 // bool m_buffFilled; 0207 }; 0208 0209 /*class HistoryScrollBufferV2 : public HistoryScroll 0210 { 0211 public: 0212 virtual int getLines(); 0213 virtual int getLineLen(int lineno); 0214 virtual void getCells(int lineno, int colno, int count, Character res[]); 0215 virtual bool isWrappedLine(int lineno); 0216 0217 virtual void addCells(const Character a[], int count); 0218 virtual void addCells(const QVector<Character>& cells); 0219 virtual void addLine(bool previousWrapped=false); 0220 0221 };*/ 0222 0223 #endif 0224 0225 ////////////////////////////////////////////////////////////////////// 0226 // Nothing-based history (no history :-) 0227 ////////////////////////////////////////////////////////////////////// 0228 class HistoryScrollNone : public HistoryScroll 0229 { 0230 public: 0231 HistoryScrollNone(); 0232 ~HistoryScrollNone() override; 0233 0234 bool hasScroll() override; 0235 0236 int getLines() override; 0237 int getLineLen(int lineno) override; 0238 void getCells(int lineno, int colno, int count, std::span<Character> res) override; 0239 bool isWrappedLine(int lineno) override; 0240 0241 void addCells(std::span<const Character> a, int count) override; 0242 void addLine(bool previousWrapped = false) override; 0243 }; 0244 0245 ////////////////////////////////////////////////////////////////////// 0246 // BlockArray-based history 0247 ////////////////////////////////////////////////////////////////////// 0248 class HistoryScrollBlockArray : public HistoryScroll 0249 { 0250 public: 0251 HistoryScrollBlockArray(size_t size); 0252 ~HistoryScrollBlockArray() override; 0253 0254 int getLines() override; 0255 int getLineLen(int lineno) override; 0256 void getCells(int lineno, int colno, int count, std::span<Character> res) override; 0257 bool isWrappedLine(int lineno) override; 0258 0259 void addCells(std::span<const Character> a, int count) override; 0260 void addLine(bool previousWrapped = false) override; 0261 0262 protected: 0263 BlockArray m_blockArray; 0264 QHash<int, size_t> m_lineLengths; 0265 }; 0266 0267 ////////////////////////////////////////////////////////////////////// 0268 // History using compact storage 0269 // This implementation uses a list of fixed-sized blocks 0270 // where history lines are allocated in (avoids heap fragmentation) 0271 ////////////////////////////////////////////////////////////////////// 0272 typedef QVector<Character> TextLine; 0273 0274 class CharacterFormat 0275 { 0276 public: 0277 bool equalsFormat(const CharacterFormat &other) const 0278 { 0279 return other.rendition == rendition && other.fgColor == fgColor && other.bgColor == bgColor; 0280 } 0281 0282 bool equalsFormat(const Character &c) const 0283 { 0284 return c.rendition == rendition && c.foregroundColor == fgColor && c.backgroundColor == bgColor; 0285 } 0286 0287 void setFormat(const Character &c) 0288 { 0289 rendition = c.rendition; 0290 fgColor = c.foregroundColor; 0291 bgColor = c.backgroundColor; 0292 } 0293 0294 CharacterColor fgColor, bgColor; 0295 quint16 startPos; 0296 quint8 rendition; 0297 }; 0298 0299 class CompactHistoryBlock 0300 { 0301 public: 0302 CompactHistoryBlock() 0303 { 0304 blockLength = 4096 * 64; // 256kb 0305 head = (quint8 *)mmap(nullptr, blockLength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 0306 // head = (quint8*) malloc(blockLength); 0307 Q_ASSERT(head != MAP_FAILED); 0308 tail = blockStart = head; 0309 allocCount = 0; 0310 } 0311 0312 virtual ~CompactHistoryBlock() 0313 { 0314 // free(blockStart); 0315 munmap(blockStart, blockLength); 0316 } 0317 0318 virtual unsigned int remaining() 0319 { 0320 return blockStart + blockLength - tail; 0321 } 0322 virtual unsigned length() 0323 { 0324 return blockLength; 0325 } 0326 virtual void *allocate(size_t length); 0327 virtual bool contains(void *addr) 0328 { 0329 return addr >= blockStart && addr < (blockStart + blockLength); 0330 } 0331 virtual void deallocate(); 0332 virtual bool isInUse() 0333 { 0334 return allocCount != 0; 0335 }; 0336 0337 private: 0338 size_t blockLength; 0339 quint8 *head; 0340 quint8 *tail; 0341 quint8 *blockStart; 0342 int allocCount; 0343 }; 0344 0345 class CompactHistoryBlockList 0346 { 0347 public: 0348 CompactHistoryBlockList(){}; 0349 ~CompactHistoryBlockList(); 0350 0351 void *allocate(size_t size); 0352 void deallocate(void *); 0353 int length() 0354 { 0355 return list.size(); 0356 } 0357 0358 private: 0359 QList<CompactHistoryBlock *> list; 0360 }; 0361 0362 class CompactHistoryLine 0363 { 0364 public: 0365 CompactHistoryLine(const TextLine &, CompactHistoryBlockList &blockList); 0366 virtual ~CompactHistoryLine(); 0367 0368 // custom new operator to allocate memory from custom pool instead of heap 0369 static void *operator new(size_t size, CompactHistoryBlockList &blockList); 0370 static void operator delete(void *){/* do nothing, deallocation from pool is done in destructor*/}; 0371 0372 virtual void getCharacters(Character *array, int length, int startColumn); 0373 virtual void getCharacter(int index, Character &r); 0374 virtual bool isWrapped() const 0375 { 0376 return wrapped; 0377 }; 0378 virtual void setWrapped(bool isWrapped) 0379 { 0380 wrapped = isWrapped; 0381 }; 0382 virtual unsigned int getLength() const 0383 { 0384 return length; 0385 }; 0386 0387 protected: 0388 CompactHistoryBlockList &blockList; 0389 CharacterFormat *formatArray; 0390 quint16 length; 0391 quint16 *text; 0392 quint16 formatLength; 0393 bool wrapped; 0394 }; 0395 0396 class CompactHistoryScroll : public HistoryScroll 0397 { 0398 typedef QList<CompactHistoryLine *> HistoryArray; 0399 0400 public: 0401 CompactHistoryScroll(unsigned int maxNbLines = 1000); 0402 ~CompactHistoryScroll() override; 0403 0404 int getLines() override; 0405 int getLineLen(int lineno) override; 0406 void getCells(int lineno, int colno, int count, std::span<Character> res) override; 0407 bool isWrappedLine(int lineno) override; 0408 0409 void addCells(std::span<const Character> a, int count) override; 0410 void addCellsVector(const TextLine &cells) override; 0411 void addLine(bool previousWrapped = false) override; 0412 0413 void setMaxNbLines(unsigned int nbLines); 0414 unsigned int maxNbLines() const 0415 { 0416 return _maxLineCount; 0417 } 0418 0419 private: 0420 bool hasDifferentColors(const TextLine &line) const; 0421 HistoryArray lines; 0422 CompactHistoryBlockList blockList; 0423 0424 unsigned int _maxLineCount; 0425 }; 0426 0427 ////////////////////////////////////////////////////////////////////// 0428 // History type 0429 ////////////////////////////////////////////////////////////////////// 0430 0431 class HistoryType 0432 { 0433 public: 0434 HistoryType(); 0435 virtual ~HistoryType(); 0436 0437 /** 0438 * Returns true if the history is enabled ( can store lines of output ) 0439 * or false otherwise. 0440 */ 0441 virtual bool isEnabled() const = 0; 0442 /** 0443 * Returns true if the history size is unlimited. 0444 */ 0445 bool isUnlimited() const 0446 { 0447 return maximumLineCount() == 0; 0448 } 0449 /** 0450 * Returns the maximum number of lines which this history type 0451 * can store or 0 if the history can store an unlimited number of lines. 0452 */ 0453 virtual int maximumLineCount() const = 0; 0454 0455 virtual std::unique_ptr<HistoryScroll> scroll(std::unique_ptr<HistoryScroll> &&) const = 0; 0456 }; 0457 0458 class HistoryTypeNone : public HistoryType 0459 { 0460 public: 0461 HistoryTypeNone(); 0462 0463 bool isEnabled() const override; 0464 int maximumLineCount() const override; 0465 0466 std::unique_ptr<HistoryScroll> scroll(std::unique_ptr<HistoryScroll> &&) const override; 0467 }; 0468 0469 class HistoryTypeBlockArray : public HistoryType 0470 { 0471 public: 0472 HistoryTypeBlockArray(size_t size); 0473 0474 bool isEnabled() const override; 0475 int maximumLineCount() const override; 0476 0477 std::unique_ptr<HistoryScroll> scroll(std::unique_ptr<HistoryScroll> &&) const override; 0478 0479 protected: 0480 size_t m_size; 0481 }; 0482 0483 #if 1 0484 class HistoryTypeFile : public HistoryType 0485 { 0486 public: 0487 HistoryTypeFile(const QString &fileName = QString()); 0488 0489 bool isEnabled() const override; 0490 virtual const QString &getFileName() const; 0491 int maximumLineCount() const override; 0492 0493 std::unique_ptr<HistoryScroll> scroll(std::unique_ptr<HistoryScroll> &&) const override; 0494 0495 protected: 0496 QString m_fileName; 0497 }; 0498 0499 class HistoryTypeBuffer : public HistoryType 0500 { 0501 friend class HistoryScrollBuffer; 0502 0503 public: 0504 HistoryTypeBuffer(unsigned int nbLines); 0505 0506 bool isEnabled() const override; 0507 int maximumLineCount() const override; 0508 0509 std::unique_ptr<HistoryScroll> scroll(std::unique_ptr<HistoryScroll> &&) const override; 0510 0511 protected: 0512 unsigned int m_nbLines; 0513 }; 0514 0515 class CompactHistoryType : public HistoryType 0516 { 0517 public: 0518 CompactHistoryType(unsigned int size); 0519 0520 bool isEnabled() const override; 0521 int maximumLineCount() const override; 0522 0523 std::unique_ptr<HistoryScroll> scroll(std::unique_ptr<HistoryScroll> &&) const override; 0524 0525 protected: 0526 unsigned int m_nbLines; 0527 }; 0528 0529 #endif 0530 0531 } 0532 0533 #endif // TEHISTORY_H