From 77283241a9e993935b6517677da606dabee46713 Mon Sep 17 00:00:00 2001 From: Wilhelm Schuermann Date: Thu, 12 Nov 2015 16:14:29 +0100 Subject: [PATCH] Nibbler: Improve getQuoted() performance Improves "load" time for all performance tests that load data by ~20%. - "next" down 15% total - "list" down 7% total - "all" down 3% total - "add" down 15% total - "export" down 8% total - "import" down 6% total --- src/Nibbler.cpp | 65 ++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/Nibbler.cpp b/src/Nibbler.cpp index b42844d91..bf6fb3d79 100644 --- a/src/Nibbler.cpp +++ b/src/Nibbler.cpp @@ -180,12 +180,11 @@ bool Nibbler::getN (const int quantity, std::string& result) } //////////////////////////////////////////////////////////////////////////////// +// Gets quote content: "foobar" -> foobar (for c = '"') +// Handles escaped quotes: "foo\"bar" -> foo\"bar (for c = '"') +// Returns false if first character is not c, or if there is no closing c bool Nibbler::getQuoted (char c, std::string& result) { - bool inquote = false; - bool inescape = false; - char previous = 0; - char current = 0; result = ""; if (_cursor >= _length || @@ -194,42 +193,52 @@ bool Nibbler::getQuoted (char c, std::string& result) return false; } - for (auto i = _cursor; i < _length; ++i) + std::string::size_type start = _cursor + 1; // Skip first quote char + std::string::size_type i = start; + + while (i < _length) { - current = (*_input)[i]; + i = (*_input).find (c, i); - if (current == '\\' && !inescape) + if (i == std::string::npos) + return false; // Unclosed quote + + if (i == start) { - inescape = true; - previous = current; - continue; + // Empty quote + _cursor += 2; // Skip both quote chars + return true; } - if (current == c && !inescape) + if ((*_input)[i-1] == '\\') { - if (!inquote) + // Check for escaped backslashes. Backtracking like this is not very + // efficient, but is only done in extreme corner cases. + + auto j = i-2; // Start one character further left + bool is_escaped_quote = true; + while (j >= start && (*_input)[j] == '\\') { - inquote = true; - } - else - { - _cursor = i + 1; - return true; - } - } - else - { - if (previous) - { - result += previous; - previous = 0; + // Toggle flag for each further backslash encountered. + is_escaped_quote = is_escaped_quote ? false : true; + --j; } - result += current; - inescape = false; + if (is_escaped_quote) + { + // Keep searching + ++i; + continue; + } } + + // None of the above applied, we must have found the closing quote char. + result.assign ((*_input), start, i - start); + _cursor = i + 1; // Skip closing quote char + return true; } + // This should never be reached. We could throw here instead. return false; }