diff --git a/src/Core/RideDB.l b/src/Core/RideDB.l index 7c281b585..4b10c3cc2 100644 --- a/src/Core/RideDB.l +++ b/src/Core/RideDB.l @@ -44,20 +44,12 @@ static QString unprotect(char *string) // this is a lexer string so it will be enclosed // in quotes. Lets strip those first - QString s = string2.mid(1,string2.length()-2); + QStringRef r = string2.midRef(1,string2.length()-2); // does it end with a space (to avoid token conflict) ? - if (s.endsWith(" ")) s = s.mid(0, s.length()-1); + if (r.endsWith(" ")) r = r.mid(0, r.length()-1); - // now un-escape the control characters - s.replace("\\t", "\t"); // tab - s.replace("\\n", "\n"); // newline - s.replace("\\r", "\r"); // carriage-return - s.replace("\\b", "\b"); // backspace - s.replace("\\f", "\f"); // formfeed - s.replace("\\/", "/"); // solidus - s.replace("\\\"", "\""); // quote - s.replace("\\\\", "\\"); // backslash + QString s = Utils::RidefileUnEscape(r); return s; } diff --git a/src/Core/Utils.cpp b/src/Core/Utils.cpp index 0b5003304..05e9cad1e 100644 --- a/src/Core/Utils.cpp +++ b/src/Core/Utils.cpp @@ -22,9 +22,122 @@ #include #include #include +#include namespace Utils { + +// Class for performing multiple string substitutions in a single +// pass over string. This implementation is only valid when substitutions +// will not inject additional substitution opportunities. +class StringSubstitutionizer +{ + QVector v; + QMap qm; + QRegularExpression qr; + + QString GetSubstitute(QString s) const + { + if (!qm.contains(s)) return QString(); + return qm.value(s); + } + + void BuildFindAnyRegex() + { + QString qRegexString; + + for (int i = 0; i < v.size(); i++) + { + if (i > 0) qRegexString.append("|"); + + qRegexString.append(v[i]); + } + + qr = QRegularExpression(qRegexString); + } + +protected: + + void PushSubstitution(QString regexstring, QString matchstring, QString substitute) + { + if (!qm.contains(matchstring)) + { + v.push_back(regexstring); + qm[matchstring] = substitute; + } + } + + void FinalizeInit() + { + BuildFindAnyRegex(); + } + + QRegularExpression GetFindAnyRegex() const + { + return qr; + } + +public: + + QString BuildSubstitutedString(QStringRef s) const + { + QRegularExpression qr = GetFindAnyRegex(); + + QRegularExpressionMatchIterator i = qr.globalMatch(s); + + if (!i.hasNext()) + return s.toString(); + + QString newstring; + + unsigned iCopyIdx = 0; + do + { + QRegularExpressionMatch match = i.next(); + int copysize = match.capturedStart() - iCopyIdx; + + if (copysize > 0) + newstring.append(s.mid(iCopyIdx, copysize)); + + newstring.append(GetSubstitute(match.captured())); + + iCopyIdx = (match.capturedStart() + match.captured().size()); + } while (i.hasNext()); + + int copysize = s.size() - iCopyIdx; + if (copysize > 0) + newstring.append(s.mid(iCopyIdx, copysize)); + + return newstring; + } +}; + +struct RidefileUnEscaper : public StringSubstitutionizer +{ + RidefileUnEscaper() + { + // regex match replacement + PushSubstitution("\\\\t", "\\t", "\t"); // tab + PushSubstitution("\\\\n", "\\n", "\n"); // newline + PushSubstitution("\\\\r", "\\r", "\r"); // carriage-return + PushSubstitution("\\\\b", "\\b", "\b"); // backspace + PushSubstitution("\\\\f", "\\f", "\f"); // formfeed + PushSubstitution("\\\\/", "\\/", "/"); // solidus + PushSubstitution("\\\\\"", "\\\"", "\""); // quote + PushSubstitution("\\\\\\\\", "\\\\", "\\"); // backslash + + FinalizeInit(); + } +}; + +QString RidefileUnEscape(const QStringRef s) +{ + // Static const object constructs it's search regex at load time. + static const RidefileUnEscaper s_RidefileUnescaper; + + return s_RidefileUnescaper.BuildSubstitutedString(s); +} + // when writing xml... QString xmlprotect(const QString &string) { @@ -43,7 +156,7 @@ QString xmlprotect(const QString &string) return s; } -// BEWARE: this function is tide closely to RideFile parsing +// BEWARE: this function is tied closely to RideFile parsing // DO NOT CHANGE IT UNLESS YOU KNOW WHAT YOU ARE DOING QString unprotect(const QString &buffer) { diff --git a/src/FileIO/JsonRideFile.h b/src/FileIO/JsonRideFile.h index 98e8acc6c..4eff68653 100644 --- a/src/FileIO/JsonRideFile.h +++ b/src/FileIO/JsonRideFile.h @@ -29,6 +29,9 @@ #include #define DATETIME_FORMAT "yyyy/MM/dd hh:mm:ss' UTC'" +namespace Utils { + QString RidefileUnEscape(QStringRef); +}; struct JsonFileReader : public RideFileReader { virtual RideFile *openRideFile(QFile &file, QStringList &errors, QList* = 0) const; diff --git a/src/FileIO/JsonRideFile.l b/src/FileIO/JsonRideFile.l index 6aeebc07b..4a335c5f3 100644 --- a/src/FileIO/JsonRideFile.l +++ b/src/FileIO/JsonRideFile.l @@ -44,20 +44,12 @@ static QString unprotect(char *string) // this is a lexer string so it will be enclosed // in quotes. Lets strip those first - QString s = string2.mid(1,string2.length()-2); + QStringRef r = string2.midRef(1,string2.length()-2); // does it end with a space (to avoid token conflict) ? - if (s.endsWith(" ")) s = s.mid(0, s.length()-1); + if (r.endsWith(" ")) r = r.mid(0, r.length()-1); - // now un-escape the control characters - s.replace("\\t", "\t"); // tab - s.replace("\\n", "\n"); // newline - s.replace("\\r", "\r"); // carriage-return - s.replace("\\b", "\b"); // backspace - s.replace("\\f", "\f"); // formfeed - s.replace("\\/", "/"); // solidus - s.replace("\\\"", "\""); // quote - s.replace("\\\\", "\\"); // backslash + QString s = Utils::RidefileUnEscape(r); return s; }