File indexing completed on 2024-05-05 05:53:43

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // Own
0008 #include "ExtendedCharTable.h"
0009 
0010 #include "charactersdebug.h"
0011 
0012 using namespace Konsole;
0013 
0014 ExtendedCharTable::ExtendedCharTable() = default;
0015 
0016 ExtendedCharTable::~ExtendedCharTable()
0017 {
0018     // free all allocated character buffers
0019     QHashIterator<uint, char32_t *> iter(_extendedCharTable);
0020     while (iter.hasNext()) {
0021         iter.next();
0022         delete[] iter.value();
0023     }
0024 }
0025 
0026 // global instance
0027 ExtendedCharTable ExtendedCharTable::instance;
0028 
0029 char32_t ExtendedCharTable::createExtendedChar(const char32_t *unicodePoints, ushort length, const pExtendedChars extendedChars)
0030 {
0031     // look for this sequence of points in the table
0032     uint hash = extendedCharHash(unicodePoints, length);
0033     const uint initialHash = hash;
0034     bool triedCleaningSolution = false;
0035 
0036     // check existing entry for match
0037     while (_extendedCharTable.contains(hash) && hash != 0) { // 0 has a special meaning for chars so we don't use it
0038         if (extendedCharMatch(hash, unicodePoints, length)) {
0039             // this sequence already has an entry in the table,
0040             // return its hash
0041             return hash;
0042         }
0043         // if hash is already used by another, different sequence of unicode character
0044         // points then try next hash
0045         hash++;
0046 
0047         if (hash == initialHash) {
0048             if (!triedCleaningSolution) {
0049                 triedCleaningSolution = true;
0050                 // All the hashes are full, go to all Screens and try to free any
0051                 // This is slow but should happen very rarely
0052                 QSet<uint> usedExtendedChars = extendedChars();
0053 
0054                 QHash<uint, char32_t *>::iterator it = _extendedCharTable.begin();
0055                 QHash<uint, char32_t *>::iterator itEnd = _extendedCharTable.end();
0056                 while (it != itEnd) {
0057                     if (usedExtendedChars.contains(it.key())) {
0058                         ++it;
0059                     } else {
0060                         it = _extendedCharTable.erase(it);
0061                     }
0062                 }
0063             } else {
0064                 qCDebug(CharactersDebug) << "Using all the extended char hashes, going to miss this extended character";
0065                 return 0;
0066             }
0067         }
0068     }
0069 
0070     // add the new sequence to the table and
0071     // return that index
0072     auto buffer = new char32_t[length + 1];
0073     buffer[0] = length;
0074     std::copy_n(unicodePoints, length, &buffer[1]);
0075 
0076     _extendedCharTable.insert(hash, buffer);
0077 
0078     return hash;
0079 }
0080 
0081 char32_t *ExtendedCharTable::lookupExtendedChar(uint hash, ushort &length) const
0082 {
0083     // look up index in table and if found, set the length
0084     // argument and return a pointer to the character sequence
0085 
0086     char32_t *buffer = _extendedCharTable[hash];
0087     if (buffer != nullptr) {
0088         length = ushort(buffer[0]);
0089         return buffer + 1;
0090     }
0091     length = 0;
0092     return nullptr;
0093 }
0094 
0095 uint ExtendedCharTable::extendedCharHash(const char32_t *unicodePoints, ushort length) const
0096 {
0097     uint hash = 0;
0098     for (ushort i = 0; i < length; i++) {
0099         hash = 31 * hash + unicodePoints[i];
0100     }
0101     return hash;
0102 }
0103 
0104 bool ExtendedCharTable::extendedCharMatch(uint hash, const char32_t *unicodePoints, ushort length) const
0105 {
0106     char32_t *entry = _extendedCharTable[hash];
0107 
0108     // compare given length with stored sequence length ( given as the first ushort in the
0109     // stored buffer )
0110     if (entry == nullptr || entry[0] != length) {
0111         return false;
0112     }
0113     // if the lengths match, each character must be checked.  the stored buffer starts at
0114     // entry[1]
0115     for (int i = 0; i < length; i++) {
0116         if (entry[i + 1] != unicodePoints[i]) {
0117             return false;
0118         }
0119     }
0120     return true;
0121 }