Database: Add forward/reverse iterator

This allows the database to be treated as a single collection of strings, but
can be used to avoid loading the entire database when only interested in recent
entries.

Related to issue #245.
This commit is contained in:
Shaun Ruffell 2019-12-23 04:04:45 -06:00 committed by lauft
parent ea3bbd0e85
commit 557fd4cb34
2 changed files with 245 additions and 0 deletions

View file

@ -24,6 +24,7 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <cassert>
#include <Database.h>
#include <format.h>
#include <JSON.h>
@ -32,6 +33,191 @@
#include <shared.h>
#include <timew.h>
////////////////////////////////////////////////////////////////////////////////
Database::iterator::iterator (files_iterator fbegin, files_iterator fend) :
files_it(fbegin),
files_end(fend)
{
if (files_end != files_it) {
auto &lines = files_it->allLines ();
lines_it = lines.begin ();
lines_end = lines.end ();
while ((lines_it == lines_end) && (files_it != files_end))
++files_it;
}
}
////////////////////////////////////////////////////////////////////////////////
Database::iterator& Database::iterator::operator++()
{
if (files_it != files_end)
{
if (lines_it != lines_end)
{
++lines_it;
// If we are at the end of the current file, we will need to advance to
// the next file here. A file may be empty, which is why we need to wait
// until we are pointing at a valid line.
while ((lines_it == lines_end) && (files_it != files_end))
{
++files_it;
if (files_it != files_end)
{
auto& lines = files_it->allLines ();
lines_it = lines.begin ();
lines_end = lines.end ();
}
}
}
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
bool Database::iterator::operator==(const iterator & other) const
{
return (other.files_it == other.files_end) ?
files_it == files_end :
((files_it == other.files_it) &&
(files_end == other.files_end) &&
(lines_it == other.lines_it) &&
(lines_end == other.lines_end));
}
////////////////////////////////////////////////////////////////////////////////
bool Database::iterator::operator!=(const iterator & other) const
{
return ! (*this == other);
}
////////////////////////////////////////////////////////////////////////////////
const std::string& Database::iterator::operator*() const
{
assert(lines_it != lines_end);
return *lines_it;
}
////////////////////////////////////////////////////////////////////////////////
const std::string* Database::iterator::operator->() const
{
assert(lines_it != lines_end);
return &(*lines_it);
}
////////////////////////////////////////////////////////////////////////////////
Database::reverse_iterator::reverse_iterator (files_iterator fbegin,
files_iterator fend) :
files_it(fbegin),
files_end(fend)
{
if (files_end != files_it)
{
lines_it = files_it->allLines ().rbegin ();
lines_end = files_it->allLines ().rend ();
while ((lines_it == lines_end) && (files_it != files_end))
++files_it;
}
}
////////////////////////////////////////////////////////////////////////////////
Database::reverse_iterator& Database::reverse_iterator::operator++()
{
if (files_it != files_end)
{
if (lines_it != lines_end)
{
++lines_it;
// If we are at the end of the current file, we will need to advance to
// the next file here. A file may be empty, which is why we need to wait
// until we are pointing at a valid line.
while ((lines_it == lines_end) && (files_it != files_end))
{
++files_it;
if (files_it != files_end)
{
lines_it = files_it->allLines ().rbegin ();
lines_end = files_it->allLines ().rend ();
}
}
}
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
bool
Database::reverse_iterator::operator==(const reverse_iterator & other) const
{
return (other.files_it == other.files_end) ?
files_it == files_end :
((files_it == other.files_it) &&
(files_end == other.files_end) &&
(lines_it == other.lines_it) &&
(lines_end == other.lines_end));
}
////////////////////////////////////////////////////////////////////////////////
bool
Database::reverse_iterator::operator!=(const reverse_iterator & other) const
{
return ! (*this == other);
}
////////////////////////////////////////////////////////////////////////////////
const std::string& Database::reverse_iterator::operator*() const
{
assert (lines_it != lines_end);
return *lines_it;
}
////////////////////////////////////////////////////////////////////////////////
const std::string* Database::reverse_iterator::operator->() const
{
return &operator*();
}
////////////////////////////////////////////////////////////////////////////////
Database::iterator Database::begin ()
{
if (_files.empty ())
{
initializeDatafiles ();
}
return iterator(_files.begin (), _files.end ());
}
////////////////////////////////////////////////////////////////////////////////
Database::iterator Database::end ()
{
return iterator (_files.end (), _files.end ());
}
////////////////////////////////////////////////////////////////////////////////
Database::reverse_iterator Database::rbegin ()
{
if (_files.empty ())
{
initializeDatafiles ();
}
return reverse_iterator (_files.rbegin (), _files.rend ());
}
////////////////////////////////////////////////////////////////////////////////
Database::reverse_iterator Database::rend ()
{
if (_files.empty ())
{
initializeDatafiles ();
}
return reverse_iterator (_files.rend (), _files.rend ());
}
////////////////////////////////////////////////////////////////////////////////
void Database::initialize (const std::string& location, Journal& journal)
{

View file

@ -38,6 +38,60 @@
class Database
{
public:
class iterator
{
private:
friend class Database;
typedef std::vector <Datafile>::iterator files_iterator;
typedef std::vector <std::string>::const_iterator lines_iterator;
typedef std::string value_type;
files_iterator files_it;
files_iterator files_end;
lines_iterator lines_it;
lines_iterator lines_end;
iterator (files_iterator fbegin, files_iterator fend);
public:
iterator& operator++ ();
iterator& operator++ (int);
iterator& operator-- ();
bool operator== (const iterator & other) const;
bool operator!= (const iterator & other) const;
const value_type& operator* () const;
const value_type* operator-> () const;
};
class reverse_iterator
{
private:
friend class Database;
typedef std::vector <Datafile>::reverse_iterator files_iterator;
typedef std::vector <std::string>::const_reverse_iterator lines_iterator;
typedef std::string value_type;
files_iterator files_it;
files_iterator files_end;
lines_iterator lines_it;
lines_iterator lines_end;
reverse_iterator(files_iterator fbegin, files_iterator fend);
public:
reverse_iterator& operator++ ();
reverse_iterator& operator++ (int);
reverse_iterator& operator-- ();
bool operator== (const reverse_iterator & other) const;
bool operator!= (const reverse_iterator & other) const;
const value_type& operator* () const;
const value_type* operator-> () const;
};
public:
Database () = default;
void initialize (const std::string&, Journal& journal);
@ -53,6 +107,11 @@ public:
std::string dump () const;
iterator begin ();
iterator end ();
reverse_iterator rbegin ();
reverse_iterator rend ();
private:
unsigned int getDatafile (int, int);
std::vector <Range> segmentRange (const Range&);