Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for library reloading #636

Merged
merged 24 commits into from
Nov 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0e48baf
Simplified Library::getReaderById()
veloman-yunkan Oct 20, 2021
913a368
Made Manager's ctors explicit
veloman-yunkan Nov 20, 2021
571e417
Manager is now safe to copy
veloman-yunkan Nov 20, 2021
3296a02
Testing of Book::getHumanReadableIdFromPath()
veloman-yunkan Nov 22, 2021
339f845
Bugfix in Book::getHumanReadableIdFromPath()
veloman-yunkan Nov 22, 2021
d62c4fd
Testing of HumanReadableNameMapper
veloman-yunkan Nov 22, 2021
5f3c34e
NameMapper's API is now const
veloman-yunkan Nov 22, 2021
4ccbdcb
Code deduplication in NameMapperTest
veloman-yunkan Nov 22, 2021
8fffa59
Added NameMapperProxy from kiwix/kiwix-desktop#714
veloman-yunkan Nov 22, 2021
6199c11
NameMapperProxy respects the withAlias flag
veloman-yunkan Nov 21, 2021
2d6a7fe
Testing of NameMapperProxy
veloman-yunkan Nov 22, 2021
76a5e3a
Library::addBook() updates the reader cache
veloman-yunkan Nov 20, 2021
226dac2
LibraryManipulator is now merely a notifier
veloman-yunkan Nov 21, 2021
3aeeeee
Manager::reload()
veloman-yunkan Nov 27, 2021
298247c
Renamed NameMapperProxy -> UpdatableNameMapper
veloman-yunkan Nov 27, 2021
b712c73
Dropped Library::getBookBy*() non-const functions
veloman-yunkan Nov 28, 2021
c2927ce
Library got a yet unused mutex
veloman-yunkan Nov 28, 2021
02b9e32
Library became almost thread-safe
veloman-yunkan Nov 28, 2021
473d2d2
Introduced Library::getBookByIdThreadSafe()
veloman-yunkan Nov 28, 2021
ad2eb52
Thread safe dumping of the OPDS feed
veloman-yunkan Nov 28, 2021
1d53834
Noted a potential bug in Library::addBook()
veloman-yunkan Nov 28, 2021
262e138
Enter Library::removeBooksNotUpdatedSince()
veloman-yunkan Nov 28, 2021
7161db8
Manager::reload() also removes books from Library
veloman-yunkan Nov 28, 2021
405ea29
Added Library::addOrUpdateBook() alias
veloman-yunkan Nov 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 65 additions & 6 deletions include/library.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <vector>
#include <map>
#include <memory>
#include <mutex>
#include <zim/archive.h>

#include "book.h"
Expand Down Expand Up @@ -139,20 +140,51 @@ class Filter {
bool accept(const Book& book) const;
};


/**
* A Library store several books.
* This class is not part of the libkiwix API. Its only purpose is
* to simplify the implementation of the Library's move operations
* and avoid bugs should new data members be added to Library.
*/
class Library
class LibraryBase
{
std::map<std::string, kiwix::Book> m_books;
protected: // types
typedef uint64_t LibraryRevision;

struct Entry : Book
{
LibraryRevision lastUpdatedRevision = 0;

// May also keep the Archive and Reader pointers here and get
// rid of the m_readers and m_archives data members in Library
};

protected: // data
LibraryRevision m_revision;
std::map<std::string, Entry> m_books;
std::map<std::string, std::shared_ptr<Reader>> m_readers;
std::map<std::string, std::shared_ptr<zim::Archive>> m_archives;
std::vector<kiwix::Bookmark> m_bookmarks;
class BookDB;
std::unique_ptr<BookDB> m_bookDB;

protected: // functions
LibraryBase();
~LibraryBase();

LibraryBase(LibraryBase&& );
LibraryBase& operator=(LibraryBase&& );
};

/**
* A Library store several books.
*/
class Library : private LibraryBase
{
// all data fields must be added in LibraryBase
mutable std::mutex m_mutex;

public:
typedef LibraryRevision Revision;
typedef std::vector<std::string> BookIdCollection;
typedef std::map<std::string, int> AttributeCounts;

Expand Down Expand Up @@ -180,6 +212,11 @@ class Library
*/
bool addBook(const Book& book);

/**
* A self-explanatory alias for addBook()
*/
bool addOrUpdateBook(const Book& book) { return addBook(book); }

/**
* Add a bookmark to the library.
*
Expand All @@ -196,10 +233,13 @@ class Library
*/
bool removeBookmark(const std::string& zimId, const std::string& url);

// XXX: This is a non-thread-safe operation
const Book& getBookById(const std::string& id) const;
Book& getBookById(const std::string& id);
// XXX: This is a non-thread-safe operation
const Book& getBookByPath(const std::string& path) const;
Book& getBookByPath(const std::string& path);

Book getBookByIdThreadSafe(const std::string& id) const;

std::shared_ptr<Reader> getReaderById(const std::string& id);
std::shared_ptr<zim::Archive> getArchiveById(const std::string& id);

Expand Down Expand Up @@ -346,6 +386,24 @@ class Library
const std::vector<std::string>& tags = {},
size_t maxSize = 0) const;

/**
* Return the current revision of the library.
*
* The revision of the library is updated (incremented by one) only by
* the addBook() operation.
*
* @return Current revision of the library.
*/
LibraryRevision getRevision() const;

/**
* Remove books that have not been updated since the specified revision.
*
* @param rev the library revision to use
* @return Count of books that were removed by this operation.
*/
uint32_t removeBooksNotUpdatedSince(LibraryRevision rev);

friend class OPDSDumper;
friend class libXMLDumper;

Expand All @@ -357,6 +415,7 @@ class Library
std::vector<std::string> getBookPropValueSet(BookStrPropMemFn p) const;
BookIdCollection filterViaBookDB(const Filter& filter) const;
void updateBookDB(const Book& book);
void dropReader(const std::string& bookId);
};

}
Expand Down
63 changes: 38 additions & 25 deletions include/manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <string>
#include <vector>
#include <memory>

namespace pugi {
class xml_document;
Expand All @@ -34,48 +35,61 @@ class xml_document;
namespace kiwix
{

class LibraryManipulator {
public:
virtual ~LibraryManipulator() {}
virtual bool addBookToLibrary(Book book) = 0;
virtual void addBookmarkToLibrary(Bookmark bookmark) = 0;
};
class LibraryManipulator
{
public: // functions
explicit LibraryManipulator(Library* library);
virtual ~LibraryManipulator();

Library& getLibrary() const { return library; }

class DefaultLibraryManipulator : public LibraryManipulator {
public:
DefaultLibraryManipulator(Library* library) :
library(library) {}
virtual ~DefaultLibraryManipulator() {}
bool addBookToLibrary(Book book) {
return library->addBook(book);
}
void addBookmarkToLibrary(Bookmark bookmark) {
library->addBookmark(bookmark);
}
private:
kiwix::Library* library;
bool addBookToLibrary(const Book& book);
void addBookmarkToLibrary(const Bookmark& bookmark);
uint32_t removeBooksNotUpdatedSince(Library::Revision rev);

protected: // overrides
virtual void bookWasAddedToLibrary(const Book& book);
virtual void bookmarkWasAddedToLibrary(const Bookmark& bookmark);
virtual void booksWereRemovedFromLibrary();

private: // data
kiwix::Library& library;
};

/**
* A tool to manage a `Library`.
*/
class Manager
{
public:
Manager(LibraryManipulator* manipulator);
Manager(Library* library);
~Manager();
public: // types
typedef std::vector<std::string> Paths;

public: // functions
explicit Manager(LibraryManipulator* manipulator);
explicit Manager(Library* library);

/**
* Read a `library.xml` and add book in the file to the library.
*
* @param path The (utf8) path to the `library.xml`.
* @param readOnly Set if the libray path could be overwritten latter with
* updated content.
* @param trustLibrary use book metadata coming from XML.
* @return True if file has been properly parsed.
*/
bool readFile(const std::string& path, bool readOnly = true, bool trustLibrary = true);

/**
* Sync the contents of the library with one or more `library.xml` files.
*
* The metadata of the library files is trusted unconditionally.
* Any books not present in the input library.xml files are removed
* from the library.
*
* @param paths The (utf8) paths to the `library.xml` files.
*/
void reload(const Paths& paths);

/**
* Load a library content store in the string.
*
Expand Down Expand Up @@ -150,8 +164,7 @@ class Manager
uint64_t m_itemsPerPage = 0;

protected:
kiwix::LibraryManipulator* manipulator;
bool mustDeleteManipulator;
std::shared_ptr<kiwix::LibraryManipulator> manipulator;

bool readBookFromPath(const std::string& path, Book* book);
bool parseXmlDom(const pugi::xml_document& doc,
Expand Down
32 changes: 26 additions & 6 deletions include/name_mapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#include <string>
#include <map>
#include <memory>
#include <mutex>

namespace kiwix
{
Expand All @@ -31,15 +33,15 @@ class Library;
class NameMapper {
public:
virtual ~NameMapper() = default;
virtual std::string getNameForId(const std::string& id) = 0;
virtual std::string getIdForName(const std::string& name) = 0;
virtual std::string getNameForId(const std::string& id) const = 0;
virtual std::string getIdForName(const std::string& name) const = 0;
};


class IdNameMapper : public NameMapper {
public:
virtual std::string getNameForId(const std::string& id) { return id; };
virtual std::string getIdForName(const std::string& name) { return name; };
virtual std::string getNameForId(const std::string& id) const { return id; };
virtual std::string getIdForName(const std::string& name) const { return name; };
};

class HumanReadableNameMapper : public NameMapper {
Expand All @@ -50,11 +52,29 @@ class HumanReadableNameMapper : public NameMapper {
public:
HumanReadableNameMapper(kiwix::Library& library, bool withAlias);
virtual ~HumanReadableNameMapper() = default;
virtual std::string getNameForId(const std::string& id);
virtual std::string getIdForName(const std::string& name);
virtual std::string getNameForId(const std::string& id) const;
virtual std::string getIdForName(const std::string& name) const;
};

class UpdatableNameMapper : public NameMapper {
typedef std::shared_ptr<NameMapper> NameMapperHandle;
public:
UpdatableNameMapper(Library& library, bool withAlias);

virtual std::string getNameForId(const std::string& id) const;
virtual std::string getIdForName(const std::string& name) const;

void update();

private:
NameMapperHandle currentNameMapper() const;

private:
mutable std::mutex mutex;
Library& library;
NameMapperHandle nameMapper;
const bool withAlias;
};

}

Expand Down
2 changes: 1 addition & 1 deletion src/book.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ std::string Book::getHumanReadableIdFromPath() const
{
std::string id = m_path;
if (!id.empty()) {
kiwix::removeAccents(id);
id = kiwix::removeAccents(id);

#ifdef _WIN32
id = replaceRegex(id, "", "^.*\\\\");
Expand Down
Loading