Add BufferSearch (from arch-repo-manager) to use it in syncthingtray as well

This commit is contained in:
Martchus 2021-07-15 02:24:28 +02:00
parent db87472be9
commit 6558ff487a
4 changed files with 181 additions and 3 deletions

View File

@ -21,6 +21,7 @@ set(HEADER_FILES
io/binaryreader.h
io/binarywriter.h
io/bitreader.h
io/buffersearch.h
io/copy.h
io/inifile.h
io/path.h
@ -49,6 +50,7 @@ set(SRC_FILES
io/binaryreader.cpp
io/binarywriter.cpp
io/bitreader.cpp
io/buffersearch.cpp
io/inifile.cpp
io/path.cpp
io/nativefilestream.cpp
@ -112,8 +114,8 @@ set(META_APP_AUTHOR "Martchus")
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
set(META_APP_DESCRIPTION "Useful C++ classes and routines such as argument parser, IO and conversion utilities")
set(META_VERSION_MAJOR 5)
set(META_VERSION_MINOR 10)
set(META_VERSION_PATCH 6)
set(META_VERSION_MINOR 11)
set(META_VERSION_PATCH 0)
# find required 3rd party libraries
include(3rdParty)

80
io/buffersearch.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "./buffersearch.h"
using namespace std;
namespace CppUtilities {
/*!
* \class BufferSearch
* \brief The BufferSearch struct invokes a callback if an initially given search term occurs in consecutively provided buffers.
* \remarks
* - The class works without making internal copies of the specified buffers, except for the search result.
* - The callback is invoked after the search term has been found and one of the specified termination characters occurred. The
* search result is passed to the callback.
* - The "search result" is the data after the last character of the search term and before any of the specified termination
* characters.
* - If no termination characters are specified, the callback is invoked directly after the search term occurred (with an empty
* search result).
* - If the specified give-up term has occurred, operator() will exit early and the specified callback will not be invoked
* anymore.
* - If the callback has been invoked, operator() will exit early and the callback will not be invoked anymore (even if the
* search term occurs again). Call reset() after consuming the result within the callback to continue the search.
*/
/*!
* \brief Processes the specified \a buffer. Invokes the callback according to the remarks mentioned in the class documentation.
*/
void BufferSearch::operator()(const char *buffer, std::size_t bufferSize)
{
if (m_hasResult || (!m_giveUpTerm.empty() && m_giveUpTermIterator == m_giveUpTerm.end())) {
return;
}
for (auto i = buffer, end = buffer + bufferSize; i != end; ++i) {
const auto currentChar = *i;
if (m_searchTermIterator == m_searchTerm.end()) {
if (m_terminationChars.empty()) {
m_hasResult = true;
} else {
for (const auto &terminationChar : m_terminationChars) {
if (currentChar == terminationChar) {
m_hasResult = true;
break;
}
}
}
if (m_hasResult) {
m_callback(*this, std::move(m_result));
return;
}
m_result += currentChar;
continue;
}
if (currentChar == *m_searchTermIterator) {
++m_searchTermIterator;
} else {
m_searchTermIterator = m_searchTerm.begin();
}
if (m_giveUpTerm.empty()) {
continue;
}
if (currentChar == *m_giveUpTermIterator) {
++m_giveUpTermIterator;
} else {
m_giveUpTermIterator = m_giveUpTerm.begin();
}
}
}
/*!
* \brief Resets the search to its initial state (assuming no characters of the search term or give-up term have been found yet).
*/
void BufferSearch::reset()
{
m_searchTermIterator = m_searchTerm.begin();
m_giveUpTermIterator = m_giveUpTerm.begin();
m_terminationTermIterator = m_terminationTerm.begin();
m_hasResult = false;
m_result.clear();
}
} // namespace CppUtilities

59
io/buffersearch.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef IOUTILITIES_BUFFER_SEARCH_H
#define IOUTILITIES_BUFFER_SEARCH_H
#include "../global.h"
#include <functional>
#include <string>
#include <string_view>
namespace CppUtilities {
class CPP_UTILITIES_EXPORT BufferSearch {
public:
using CallbackType = std::function<void(BufferSearch &, std::string &&)>;
BufferSearch(std::string_view searchTerm, std::string_view terminationChars, std::string_view giveUpTerm, CallbackType &&callback);
void operator()(std::string_view buffer);
void operator()(const char *buffer, std::size_t bufferSize);
void reset();
private:
const std::string_view m_searchTerm;
const std::string_view m_terminationChars;
const std::string_view m_terminationTerm;
const std::string_view m_giveUpTerm;
const CallbackType m_callback;
std::string_view::const_iterator m_searchTermIterator;
std::string_view::const_iterator m_giveUpTermIterator;
std::string_view::const_iterator m_terminationTermIterator;
std::string m_result;
bool m_hasResult;
};
/*!
* \brief Constructs a new BufferSearch. Might be overloaded in the future.
*/
inline BufferSearch::BufferSearch(
std::string_view searchTerm, std::string_view terminationChars, std::string_view giveUpTerm, CallbackType &&callback)
: m_searchTerm(searchTerm)
, m_terminationChars(terminationChars)
, m_giveUpTerm(giveUpTerm)
, m_callback(std::move(callback))
, m_searchTermIterator(m_searchTerm.begin())
, m_giveUpTermIterator(m_giveUpTerm.begin())
, m_terminationTermIterator(m_terminationTerm.begin())
, m_hasResult(false)
{
}
/*!
* \brief Processes the specified \a buffer. Invokes the callback according to the remarks mentioned in the class documentation.
*/
inline void BufferSearch::operator()(std::string_view buffer)
{
(*this)(buffer.data(), buffer.size());
}
} // namespace CppUtilities
#endif // IOUTILITIES_BUFFER_SEARCH_H

View File

@ -7,6 +7,7 @@
#include "../io/binaryreader.h"
#include "../io/binarywriter.h"
#include "../io/bitreader.h"
#include "../io/buffersearch.h"
#include "../io/copy.h"
#include "../io/inifile.h"
#include "../io/misc.h"
@ -43,6 +44,7 @@ class IoTests : public TestFixture {
CPPUNIT_TEST(testBinaryReader);
CPPUNIT_TEST(testBinaryWriter);
CPPUNIT_TEST(testBitReader);
CPPUNIT_TEST(testBufferSearch);
CPPUNIT_TEST(testPathUtilities);
CPPUNIT_TEST(testIniFile);
CPPUNIT_TEST(testAdvancedIniFile);
@ -62,6 +64,7 @@ public:
void testBinaryReader();
void testBinaryWriter();
void testBitReader();
void testBufferSearch();
void testPathUtilities();
void testIniFile();
void testAdvancedIniFile();
@ -233,7 +236,7 @@ void IoTests::testBinaryWriter()
}
/*!
* \brief Tests the BitReader.
* \brief Tests the BitReader class.
*/
void IoTests::testBitReader()
{
@ -259,6 +262,40 @@ void IoTests::testBitReader()
CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(8 * sizeof(testData)), reader.bitsAvailable());
}
/*!
* \brief Tests the BufferSearch class.
*/
void IoTests::testBufferSearch()
{
// setup search to test
auto expectedResult = std::string();
auto hasResult = false;
auto bs = BufferSearch("Updated version: ", "\e\n", "Starting build", [&](BufferSearch &, std::string &&result) {
CPPUNIT_ASSERT_EQUAL(expectedResult, result);
CPPUNIT_ASSERT_MESSAGE("callback only invoked once", !hasResult);
hasResult = true;
});
// feed data into the search
char buffer[30];
bs(buffer, 0);
CPPUNIT_ASSERT(!hasResult);
std::strcpy(buffer, "Starting Updated");
bs(std::string_view(buffer, 16));
CPPUNIT_ASSERT(!hasResult);
std::strcpy(buffer, " version: some ");
bs(buffer, 15);
CPPUNIT_ASSERT(!hasResult);
expectedResult = "some version number";
std::strcpy(buffer, "version number\emore chars");
bs(buffer, 25);
CPPUNIT_ASSERT(hasResult);
hasResult = false;
std::strcpy(buffer, "... Starting build ...");
bs(buffer, 22);
CPPUNIT_ASSERT(!hasResult);
}
/*!
* \brief Tests fileName() and removeInvalidChars().
*/