Fixed possible crash in Text::String with clickable monospace.

This commit is contained in:
23rd 2022-01-16 10:18:19 +03:00
parent 2b87a251cd
commit d1509436b6

View file

@ -255,6 +255,7 @@ private:
int32 _flags = 0; int32 _flags = 0;
uint16 _lnkIndex = 0; uint16 _lnkIndex = 0;
uint16 _spoilerIndex = 0; uint16 _spoilerIndex = 0;
uint16 _monoIndex = 0;
EmojiPtr _emoji = nullptr; // current emoji, if current word is an emoji, or zero EmojiPtr _emoji = nullptr; // current emoji, if current word is an emoji, or zero
int32 _blockStart = 0; // offset in result, from which current parsed block is started int32 _blockStart = 0; // offset in result, from which current parsed block is started
int32 _diacs = 0; // diac chars skipped without good char int32 _diacs = 0; // diac chars skipped without good char
@ -365,14 +366,15 @@ void Parser::createBlock(int32 skipBack) {
} }
} }
_lastSkipped = false; _lastSkipped = false;
const auto lnkIndex = _monoIndex ? _monoIndex : _lnkIndex;
if (_emoji) { if (_emoji) {
_t->_blocks.push_back(Block::Emoji(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex, _spoilerIndex, _emoji)); _t->_blocks.push_back(Block::Emoji(_t->_st->font, _t->_text, _blockStart, len, _flags, lnkIndex, _spoilerIndex, _emoji));
_emoji = nullptr; _emoji = nullptr;
_lastSkipped = true; _lastSkipped = true;
} else if (newline) { } else if (newline) {
_t->_blocks.push_back(Block::Newline(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex, _spoilerIndex)); _t->_blocks.push_back(Block::Newline(_t->_st->font, _t->_text, _blockStart, len, _flags, lnkIndex, _spoilerIndex));
} else { } else {
_t->_blocks.push_back(Block::Text(_t->_st->font, _t->_text, _t->_minResizeWidth, _blockStart, len, _flags, _lnkIndex, _spoilerIndex)); _t->_blocks.push_back(Block::Text(_t->_st->font, _t->_text, _t->_minResizeWidth, _blockStart, len, _flags, lnkIndex, _spoilerIndex));
} }
_blockStart += len; _blockStart += len;
blockCreated(); blockCreated();
@ -382,7 +384,7 @@ void Parser::createBlock(int32 skipBack) {
void Parser::createSkipBlock(int32 w, int32 h) { void Parser::createSkipBlock(int32 w, int32 h) {
createBlock(); createBlock();
_t->_text.push_back('_'); _t->_text.push_back('_');
_t->_blocks.push_back(Block::Skip(_t->_st->font, _t->_text, _blockStart++, w, h, _lnkIndex, _spoilerIndex)); _t->_blocks.push_back(Block::Skip(_t->_st->font, _t->_text, _blockStart++, w, h, _monoIndex ? _monoIndex : _lnkIndex, _spoilerIndex));
blockCreated(); blockCreated();
} }
@ -408,6 +410,9 @@ void Parser::finishEntities() {
&& _t->_blocks.back()->type() != TextBlockTNewline) { && _t->_blocks.back()->type() != TextBlockTNewline) {
_newlineAwaited = true; _newlineAwaited = true;
} }
if (IsMono(*flags)) {
_monoIndex = 0;
}
} }
} else if (const auto lnkIndex = list.back().lnkIndex()) { } else if (const auto lnkIndex = list.back().lnkIndex()) {
if (_lnkIndex == *lnkIndex) { if (_lnkIndex == *lnkIndex) {
@ -479,24 +484,16 @@ bool Parser::checkEntities() {
} }
} }
const auto text = QString(entityBegin, entityLength); const auto text = QString(entityBegin, entityLength);
auto data = QString(2, QChar(0));
// End of an entity. // It is better to trim the text to identify "Sample\n" as inline.
data[0] = QChar(_waitingEntity->offset() + entityLength); const auto trimmed = text.trimmed();
const auto isSingleLine = !trimmed.isEmpty()
&& ranges::none_of(trimmed, IsNewline);
{ if (isSingleLine) {
// It is better to trim the text to identify "Sample\n" as inline. _monos.push_back({ .text = text, .type = entityType });
const auto trimmed = text.trimmed(); _monoIndex = _monos.size();
const auto isSingleLine = !trimmed.isEmpty()
&& ranges::none_of(trimmed, IsNewline);
data[1] = QChar(isSingleLine ? 1 : 2);
} }
_monos.push_back({
.text = text,
.data = std::move(data),
.type = entityType,
});
} else if (entityType == EntityType::Url } else if (entityType == EntityType::Url
|| entityType == EntityType::Email || entityType == EntityType::Email
|| entityType == EntityType::Mention || entityType == EntityType::Mention
@ -737,7 +734,7 @@ void Parser::trimSourceRange() {
void Parser::finalize(const TextParseOptions &options) { void Parser::finalize(const TextParseOptions &options) {
_t->_links.resize(_maxLnkIndex + _maxShiftedLnkIndex); _t->_links.resize(_maxLnkIndex + _maxShiftedLnkIndex);
auto monoLnk = uint16(1); auto currentIndex = uint16(0); // Current the latest index of _t->_links.
struct { struct {
uint16 mono = 0; uint16 mono = 0;
uint16 lnk = 0; uint16 lnk = 0;
@ -753,53 +750,44 @@ void Parser::finalize(const TextParseOptions &options) {
} }
const auto shiftedIndex = block->lnkIndex(); const auto shiftedIndex = block->lnkIndex();
if (shiftedIndex <= kStringLinkIndexShift) { if (shiftedIndex <= kStringLinkIndexShift) {
if (IsMono(block->flags()) && (monoLnk <= _monos.size())) { if (IsMono(block->flags()) && shiftedIndex) {
{ const auto monoIndex = shiftedIndex;
const auto ptr = _monos[monoLnk - 1].data.constData();
const auto entityEnd = int(ptr->unicode());
const auto singleLine = (int((ptr + 1)->unicode()) == 1);
if (!singleLine) {
monoLnk++;
continue;
}
if (block->from() >= entityEnd) {
monoLnk++;
}
}
const auto monoIndex = _maxLnkIndex
+ _maxShiftedLnkIndex
+ monoLnk;
block->setLnkIndex(monoIndex);
if (lastHandlerIndex.mono == monoIndex) { if (lastHandlerIndex.mono == monoIndex) {
block->setLnkIndex(currentIndex);
continue; // Optimization. continue; // Optimization.
} else {
currentIndex++;
} }
block->setLnkIndex(currentIndex);
const auto handler = Integration::Instance().createLinkHandler( const auto handler = Integration::Instance().createLinkHandler(
_monos[monoLnk - 1], _monos[monoIndex - 1],
_context); _context);
_t->_links.resize(monoIndex); _t->_links.resize(currentIndex);
if (handler) { if (handler) {
_t->setLink(monoIndex, handler); _t->setLink(currentIndex, handler);
} }
lastHandlerIndex.mono = monoIndex; lastHandlerIndex.mono = monoIndex;
} }
continue; continue;
} }
const auto realIndex = (shiftedIndex - kStringLinkIndexShift); const auto realIndex = (shiftedIndex - kStringLinkIndexShift);
const auto index = _maxLnkIndex + realIndex; if (lastHandlerIndex.lnk == realIndex) {
block->setLnkIndex(index); block->setLnkIndex(currentIndex);
if (lastHandlerIndex.lnk == index) {
continue; // Optimization. continue; // Optimization.
} else {
currentIndex++;
} }
block->setLnkIndex(currentIndex);
// _t->_links.resize(index); _t->_links.resize(currentIndex);
const auto handler = Integration::Instance().createLinkHandler( const auto handler = Integration::Instance().createLinkHandler(
_links[realIndex - 1], _links[realIndex - 1],
_context); _context);
if (handler) { if (handler) {
_t->setLink(index, handler); _t->setLink(currentIndex, handler);
} }
lastHandlerIndex.lnk = index; lastHandlerIndex.lnk = realIndex;
} }
_t->_links.squeeze(); _t->_links.squeeze();
_t->_spoilers.squeeze(); _t->_spoilers.squeeze();