 Issue 29548581:
  Issue 4128, 5138 - Add Parser and Serializer implemented in C++ 
  Base URL: https://github.com/adblockplus/adblockpluscore.git
    
  
    Issue 29548581:
  Issue 4128, 5138 - Add Parser and Serializer implemented in C++ 
  Base URL: https://github.com/adblockplus/adblockpluscore.git
| Index: compiled/storage/Parser.cpp | 
| diff --git a/compiled/storage/Parser.cpp b/compiled/storage/Parser.cpp | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..d76ca1f60d70d279f4d5e94dd89ebbe909d90412 | 
| --- /dev/null | 
| +++ b/compiled/storage/Parser.cpp | 
| @@ -0,0 +1,183 @@ | 
| +/* | 
| +* This file is part of Adblock Plus <https://adblockplus.org/>, | 
| +* Copyright (C) 2006-present eyeo GmbH | 
| +* | 
| +* Adblock Plus is free software: you can redistribute it and/or modify | 
| +* it under the terms of the GNU General Public License version 3 as | 
| +* published by the Free Software Foundation. | 
| +* | 
| +* Adblock Plus is distributed in the hope that it will be useful, | 
| +* but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
| +* GNU General Public License for more details. | 
| +* | 
| +* You should have received a copy of the GNU General Public License | 
| +* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 
| +*/ | 
| + | 
| +#include "Parser.h" | 
| +#include "../subscription/DownloadableSubscription.h" | 
| +#include "../subscription/UserDefinedSubscription.h" | 
| +#include "../filter/Filter.h" | 
| + | 
| +namespace | 
| +{ | 
| + bool IsSection(const String& value, const String& expectedSectionName) | 
| + { | 
| + auto valueLength = value.length(); | 
| + // fast check whether it's a section and if its length is expected | 
| + if (!(valueLength == expectedSectionName.length() + 2 && value[0] == u'[' && value[valueLength - 1] == u']')) | 
| + return false; | 
| + OwnedString sectionName(DependentString(value, 1, valueLength - 2)); | 
| + sectionName.toLower(); | 
| + return sectionName == expectedSectionName; | 
| + } | 
| + | 
| + bool IsSubscriptionSection(const String& value) | 
| + { | 
| + return IsSection(value, u"subscription"_str); | 
| + } | 
| + | 
| + bool IsSubscriptionFiltersSection(const String& value) | 
| + { | 
| + return ::IsSection(value, u"subscription filters"_str); | 
| 
Wladimir Palant
2017/12/21 10:30:37
Does :: make sense here? You don't use it above.
 | 
| + } | 
| + | 
| + Subscription::KeyValue CreateKeyValue(const std::pair<DependentString, DependentString>& pair) | 
| + { | 
| + Subscription::KeyValue retValue; | 
| + retValue.first = TrimSpaces(pair.first); | 
| + retValue.second = TrimSpaces(pair.second); | 
| + return retValue; | 
| + } | 
| + | 
| + void DecodeOpeningBracket(DependentString& value) | 
| + { | 
| + String::size_type skippedChars = 0; | 
| + for (String::size_type i = 1; i < value.length(); ++i) | 
| + { | 
| + if (value[i - 1] == u'\\' && value[i] == u'[') | 
| + ++skippedChars; | 
| + value[i - skippedChars] = value[i]; | 
| + } | 
| + if (skippedChars > 0) | 
| + value.reset(value, 0, value.length() - skippedChars); | 
| + } | 
| +} | 
| + | 
| +Parser::Parser() | 
| + : mCurrentState{State::Initial} | 
| +{ | 
| +} | 
| + | 
| +void Parser::Process(const String& untrimmedLine) | 
| +{ | 
| + auto line = TrimSpaces(untrimmedLine); | 
| + // skip empty lines | 
| + if (line.length() == 0) | 
| + return; | 
| + | 
| + if (IsSubscriptionSection(line)) | 
| + { | 
| + Finalize(); | 
| + mCurrentState = State::SubscriptionSection; | 
| + return; | 
| + } | 
| + | 
| + switch (mCurrentState) | 
| + { | 
| + case State::Initial: | 
| + Initial_processLine(line); | 
| + break; | 
| + case State::SubscriptionSection: | 
| + SubscriptionSection_processLine(line); | 
| + break; | 
| + case State::SubscriptionFiltersSection: | 
| + SubscriptionFiltersSection_processLine(line); | 
| + break; | 
| + default: | 
| + ; | 
| + } | 
| +} | 
| + | 
| +void Parser::Finalize() | 
| +{ | 
| + switch (mCurrentState) | 
| + { | 
| + case State::Initial: | 
| + // don't clear file properties because they are also the parser member | 
| + break; | 
| + case State::SubscriptionSection: | 
| + onSubscription(createSubscriptionFromProperties()); | 
| + mSubscriptionProperties.clear(); | 
| + break; | 
| + case State::SubscriptionFiltersSection: | 
| + onSubscription(std::move(mSubscription)); | 
| + break; | 
| + default: | 
| + ; | 
| + } | 
| +} | 
| + | 
| +Parser::Subscriptions::size_type Parser::GetSubscriptionCount() const | 
| +{ | 
| + return mSubscriptions.size(); | 
| +} | 
| + | 
| +Subscription* Parser::SubscriptionAt(Subscriptions::size_type index) | 
| +{ | 
| + if (index >= mSubscriptions.size()) | 
| + return nullptr; | 
| + return SubscriptionPtr(mSubscriptions[index]).release(); | 
| +} | 
| + | 
| +SubscriptionPtr Parser::createSubscriptionFromProperties() const | 
| +{ | 
| + return Subscription::FromProperties(mSubscriptionProperties); | 
| +} | 
| + | 
| +void Parser::Initial_processLine(const String& line) | 
| +{ | 
| + // skip # Adblock Plus preferences | 
| + if (line[0] == u'#') | 
| + return; | 
| + | 
| + String::size_type assignSignPos = line.find(u'='); | 
| + if (assignSignPos != String::npos) | 
| + { | 
| + mFileProperties.emplace_back(CreateKeyValue(SplitString(line, assignSignPos))); | 
| + return; | 
| + } | 
| + onFail("Unexpected line value, it should be either a file property or the [Subscription] section"); | 
| +} | 
| + | 
| +void Parser::SubscriptionSection_processLine(const String& line) | 
| +{ | 
| + String::size_type assignSignPos = line.find(u'='); | 
| + if (assignSignPos != String::npos) | 
| + { | 
| + mSubscriptionProperties.emplace_back(CreateKeyValue(SplitString(line, assignSignPos))); | 
| + return; | 
| + } | 
| + if (IsSubscriptionFiltersSection(line)) | 
| + { | 
| + mSubscription = createSubscriptionFromProperties(); | 
| + mCurrentState = State::SubscriptionFiltersSection; | 
| + return; | 
| + } | 
| + onFail("Unexpected line value, it should be either a subscription property, the [Subscription filters] section or the [Subscription] section"); | 
| +} | 
| + | 
| +void Parser::SubscriptionFiltersSection_processLine(const String& line) | 
| +{ | 
| + OwnedString lineCopy{line}; | 
| 
Wladimir Palant
2017/12/21 10:30:37
I assume that you create a copy so that DecodeOpen
 | 
| + DependentString lineForFilter{lineCopy}; | 
| + DecodeOpeningBracket(lineForFilter); | 
| 
Wladimir Palant
2017/12/21 10:30:37
Copying all data around unconditionally seems wast
 | 
| + mSubscription->AddFilter(*FilterPtr(Filter::FromText(lineForFilter))); | 
| + // any line is considered as a filter, there can be no error | 
| +} | 
| + | 
| +void Parser::onSubscription(SubscriptionPtr subscription) | 
| +{ | 
| + mSubscriptions.emplace_back(std::move(subscription)); | 
| +} |