Commit 8f545694 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖

Remove more memory allocations during header parsing

Summary:
Avoid allocating dpointer when using QByteArray::fromRawData() by
introducing unfoldHeader() and Header::from7BitString() overloads
that work on const char* + size_t pair - this actually works nicely
for Structured headers which internally operate on raw char* pointers
instead of QByteArrays, so we avoid constructing an additional temporary
QByteArray in this case.

Note that this is an ABI incompatible change, make sure you rebuild
everything properly otherwise you'll get some really weird crashes.

Reviewers: vkrause

Subscribers: #kde_pim

Tags: #kde_pim

Differential Revision: https://phabricator.kde.org/D8290
parent bd3bc77e
cmake_minimum_required(VERSION 3.0)
set(PIM_VERSION "5.6.42")
set(PIM_VERSION "5.6.43")
project(KMime VERSION ${PIM_VERSION})
......
......@@ -2034,7 +2034,8 @@ Headers::Base *extractHeader(const QByteArray &head, const int headerStart, int
return nullptr;
}
const QByteArray rawType = QByteArray::fromRawData(head.constData() + headerStart, startOfFieldBody - headerStart);
const char *rawType = head.constData() + headerStart;
const size_t rawTypeLen = startOfFieldBody - headerStart;
startOfFieldBody++; //skip the ':'
if (startOfFieldBody < head.size() - 1 && head[startOfFieldBody] == ' ') { // skip the space after the ':', if there's any
......@@ -2043,21 +2044,21 @@ Headers::Base *extractHeader(const QByteArray &head, const int headerStart, int
bool folded = false;
endOfFieldBody = findHeaderLineEnd(head, startOfFieldBody, &folded);
// rawFieldBody references actual data from 'head'
QByteArray rawFieldBody = QByteArray::fromRawData(head.constData() + startOfFieldBody, endOfFieldBody - startOfFieldBody);
if (folded) {
rawFieldBody = unfoldHeader(rawFieldBody);
}
// We might get an invalid mail without a field name, don't crash on that.
if (!rawType.isEmpty()) {
header = HeaderFactory::createHeader(rawType);
if (rawTypeLen > 0) {
header = HeaderFactory::createHeader(rawType, rawTypeLen);
}
if (!header) {
//qWarning() << "Returning Generic header of type" << rawType;
header = new Headers::Generic(rawType.constData(), rawType.size());
header = new Headers::Generic(rawType, rawTypeLen);
}
if (folded) {
const auto unfoldedBody = unfoldHeader(head.constData() + startOfFieldBody, endOfFieldBody - startOfFieldBody);
header->from7BitString(unfoldedBody);
} else {
header->from7BitString(head.constData() + startOfFieldBody, endOfFieldBody - startOfFieldBody);
}
header->from7BitString(rawFieldBody);
return header;
}
......
......@@ -38,13 +38,13 @@ using namespace KMime;
using namespace KMime::Headers;
#define mk_header(hdr) \
if (qstrnicmp(type.constData(), hdr ::staticType(), type.size()) == 0) \
if (qstrnicmp(type, hdr ::staticType(), typeLen) == 0) \
return new hdr
Headers::Base *HeaderFactory::createHeader(const QByteArray &type)
Headers::Base *HeaderFactory::createHeader(const char *type, size_t typeLen)
{
Q_ASSERT(!type.isEmpty());
switch (type.at(0)) {
Q_ASSERT(type && *type);
switch (*type) {
case 'b':
case 'B':
mk_header(Bcc);
......
......@@ -35,7 +35,7 @@
#include "kmime_export.h"
class QByteArray;
#include <QByteArray>
namespace KMime
{
......@@ -47,7 +47,12 @@ class Base;
namespace HeaderFactory
{
Headers::Base *createHeader(const QByteArray &type);
Headers::Base *createHeader(const char *type, size_t typeLen);
inline Headers::Base *createHeader(const QByteArray &type)
{
return createHeader(type.constData(), type.size());
}
}
} // namespace KMime
......
......@@ -124,6 +124,11 @@ Base::~Base()
d_ptr = nullptr;
}
void Base::from7BitString(const char *s, size_t len)
{
from7BitString(QByteArray::fromRawData(s, len));
}
QByteArray Base::rfc2047Charset() const
{
if (d_ptr->encCS.isEmpty()) {
......@@ -238,14 +243,19 @@ Structured::~Structured()
d_ptr = nullptr;
}
void Structured::from7BitString(const QByteArray &s)
void Structured::from7BitString(const char *s, size_t len)
{
Q_D(Structured);
if (d->encCS.isEmpty()) {
d->encCS = Content::defaultCharset();
}
const char *cursor = s.constData();
parse(cursor, cursor + s.length());
parse(s, s + len);
}
void Structured::from7BitString(const QByteArray &s)
{
from7BitString(s.constData(), s.length());
}
QString Structured::asUnicodeString() const
......
......@@ -137,6 +137,7 @@ public:
Parses the given string. Take care of RFC2047-encoded strings.
@param s The encoded header data.
*/
virtual void from7BitString(const char *s, size_t len);
virtual void from7BitString(const QByteArray &s) = 0;
/**
......@@ -246,6 +247,7 @@ public:
Unstructured();
~Unstructured();
using Base::from7BitString;
void from7BitString(const QByteArray &s) override;
QByteArray as7BitString(bool withHeaderType = true) const override;
......@@ -298,6 +300,7 @@ public:
Structured();
~Structured();
void from7BitString(const char *s, size_t len) override;
void from7BitString(const QByteArray &s) override;
QString asUnicodeString() const override;
void fromUnicodeString(const QString &s, const QByteArray &b) override;
......
......@@ -183,35 +183,37 @@ QByteArray multiPartBoundary()
return "nextPart" + uniqueString();
}
QByteArray unfoldHeader(const QByteArray &header)
QByteArray unfoldHeader(const char *header, size_t headerSize)
{
QByteArray result;
if (header.isEmpty()) {
if (headerSize == 0) {
return result;
}
// unfolding skips characters so result will be at worst header.size() long
result.reserve(header.size());
int pos = 0, foldBegin = 0, foldMid = 0, foldEnd = 0;
while ((foldMid = header.indexOf('\n', pos)) >= 0) {
// unfolding skips characters so result will be at worst headerSize long
result.reserve(headerSize);
const char *end = header + headerSize;
const char *pos = header, *foldBegin = nullptr, *foldMid = nullptr, *foldEnd = nullptr;
while ((foldMid = strchr(pos, '\n')) && foldMid < end) {
foldBegin = foldEnd = foldMid;
// find the first space before the line-break
while (foldBegin > 0) {
if (!QChar::fromLatin1(header[foldBegin - 1]).isSpace()) {
while (foldBegin) {
if (!QChar::isSpace(*(foldBegin - 1))) {
break;
}
--foldBegin;
}
// find the first non-space after the line-break
while (foldEnd <= header.length() - 1) {
if (QChar::fromLatin1(header[foldEnd]).isSpace()) {
while (foldEnd <= end - 1) {
if (QChar::isSpace(*foldEnd)) {
++foldEnd;
} else if (foldEnd > 0 && header[foldEnd - 1] == '\n' &&
header[foldEnd] == '=' && foldEnd + 2 < header.length() &&
((header[foldEnd + 1] == '0' &&
header[foldEnd + 2] == '9') ||
(header[foldEnd + 1] == '2' &&
header[foldEnd + 2] == '0'))) {
} else if (foldEnd && *(foldEnd - 1) == '\n' &&
*foldEnd == '=' && foldEnd + 2 < (header + headerSize - 1) &&
((*(foldEnd + 1) == '0' &&
*(foldEnd + 2) == '9') ||
(*(foldEnd + 1) == '2' &&
*(foldEnd + 2) == '0'))) {
// bug #86302: malformed header continuation starting with =09/=20
foldEnd += 3;
} else {
......@@ -219,19 +221,23 @@ QByteArray unfoldHeader(const QByteArray &header)
}
}
result.append(header.constData() + pos, foldBegin - pos);
if (foldEnd < header.length() - 1) {
result.append(pos, foldBegin - pos);
if (foldEnd < end - 1) {
result += ' ';
}
pos = foldEnd;
}
const int len = header.length();
if (len > pos) {
result.append(header.constData() + pos, len - pos);
if (end > pos) {
result.append(pos, end - pos);
}
return result;
}
QByteArray unfoldHeader(const QByteArray &header)
{
return unfoldHeader(header.constData(), header.size());
}
int findHeaderLineEnd(const QByteArray &src, int &dataBegin, bool *folded)
{
int end = dataBegin;
......@@ -357,8 +363,7 @@ QByteArray extractHeader(const QByteArray &src, const QByteArray &name)
result = src.mid(begin, end - begin);
} else {
if (end > begin) {
QByteArray hdrValue = src.mid(begin, end - begin);
result = unfoldHeader(hdrValue);
result = unfoldHeader(src.constData() + begin, end - begin);
}
}
}
......
......@@ -89,7 +89,9 @@ KMIME_EXPORT extern QByteArray multiPartBoundary();
Unfolds the given header if necessary.
@param header The header to unfold.
*/
KMIME_EXPORT extern QByteArray unfoldHeader(const QByteArray &header);
KMIME_EXPORT extern QByteArray unfoldHeader(const char *header, size_t headerSize);
/**
Tries to extract the header with name @p name from the string
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment