File indexing completed on 2024-05-19 05:28:16

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