mirror of https://github.com/JakubMelka/PDF4QT.git
Visitor for performance measures
This commit is contained in:
parent
670a260265
commit
26a2a8deb5
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.7.2, 2018-11-24T18:51:27. -->
|
||||
<!-- Written by QtCreator 4.7.2, 2018-11-30T19:34:52. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
@ -67,7 +67,7 @@
|
|||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.11.2 MSVC2017 64bit</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.11.2 MSVC2017 64bit</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.qt5.5112.win64_msvc2017_64_kit</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
|
@ -296,7 +296,7 @@
|
|||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
|
||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">UnitTests/UnitTests.pro</value>
|
||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
|
||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">K:/Programming/PDF/PDF_For_Qt/bin_release/UnitTests/..</value>
|
||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">K:/Programming/PDF/PDF_For_Qt/bin_debug/UnitTests/..</value>
|
||||
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
|
@ -353,7 +353,7 @@
|
|||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
|
||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">PdfForQtViewer/PdfForQtViewer.pro</value>
|
||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
|
||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">K:/Programming/PDF/PDF_For_Qt/bin_release/PdfForQtViewer/..</value>
|
||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">K:/Programming/PDF/PDF_For_Qt/bin_debug/PdfForQtViewer/..</value>
|
||||
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
|
|
|
@ -40,7 +40,8 @@ SOURCES += \
|
|||
sources/pdfparser.cpp \
|
||||
sources/pdfdocument.cpp \
|
||||
sources/pdfdocumentreader.cpp \
|
||||
sources/pdfxreftable.cpp
|
||||
sources/pdfxreftable.cpp \
|
||||
sources/pdfvisitor.cpp
|
||||
|
||||
HEADERS += \
|
||||
sources/pdfobject.h \
|
||||
|
@ -50,7 +51,8 @@ HEADERS += \
|
|||
sources/pdfdocument.h \
|
||||
sources/pdfdocumentreader.h \
|
||||
sources/pdfxreftable.h \
|
||||
sources/pdfflatmap.h
|
||||
sources/pdfflatmap.h \
|
||||
sources/pdfvisitor.h
|
||||
|
||||
unix {
|
||||
target.path = /usr/lib
|
||||
|
|
|
@ -38,7 +38,6 @@ public:
|
|||
constexpr inline PDFObjectStorage& operator=(const PDFObjectStorage&) = default;
|
||||
constexpr inline PDFObjectStorage& operator=(PDFObjectStorage&&) = default;
|
||||
|
||||
|
||||
struct Entry
|
||||
{
|
||||
constexpr inline explicit Entry() = default;
|
||||
|
@ -50,16 +49,41 @@ public:
|
|||
|
||||
using PDFObjects = std::vector<Entry>;
|
||||
|
||||
explicit PDFObjectStorage(PDFObjects&& objects, PDFObject&& trailerDictionary) :
|
||||
m_objects(std::move(objects)),
|
||||
m_trailerDictionary(std::move(trailerDictionary))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Returns array of objects stored in this storage
|
||||
const PDFObjects& getObjects() const { return m_objects; }
|
||||
|
||||
/// Returns trailer dictionary
|
||||
const PDFObject& getTrailerDictionary() const { return m_trailerDictionary; }
|
||||
|
||||
private:
|
||||
PDFObjects m_pdfObjects;
|
||||
PDFObjects m_objects;
|
||||
PDFObject m_trailerDictionary;
|
||||
};
|
||||
|
||||
/// PDF document main class.
|
||||
class PDFDocument
|
||||
{
|
||||
public:
|
||||
explicit PDFDocument() = default;
|
||||
|
||||
const PDFObjectStorage& getStorage() const { return m_pdfObjectStorage; }
|
||||
|
||||
private:
|
||||
friend class PDFDocumentReader;
|
||||
|
||||
explicit PDFDocument(PDFObjectStorage&& storage) :
|
||||
m_pdfObjectStorage(std::move(storage))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Storage of objects
|
||||
PDFObjectStorage m_pdfObjectStorage;
|
||||
};
|
||||
|
|
|
@ -260,6 +260,9 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
|
|||
|
||||
// Now, we are ready to scan all objects
|
||||
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
|
||||
|
||||
PDFObjectStorage storage(std::move(objects), PDFObject(xrefTable.getTrailerDictionary()));
|
||||
return PDFDocument(std::move(storage));
|
||||
}
|
||||
catch (PDFParserException parserException)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
|
||||
#include "pdfobject.h"
|
||||
#include "pdfvisitor.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
@ -30,7 +31,7 @@ QByteArray PDFObject::getString() const
|
|||
return string->getString();
|
||||
}
|
||||
|
||||
const PDFDictionary*PDFObject::getDictionary() const
|
||||
const PDFDictionary* PDFObject::getDictionary() const
|
||||
{
|
||||
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
|
||||
|
||||
|
@ -38,6 +39,30 @@ const PDFDictionary*PDFObject::getDictionary() const
|
|||
return static_cast<const PDFDictionary*>(objectContent.get());
|
||||
}
|
||||
|
||||
const PDFString* PDFObject::getStringObject() const
|
||||
{
|
||||
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
|
||||
|
||||
Q_ASSERT(dynamic_cast<const PDFString*>(objectContent.get()));
|
||||
return static_cast<const PDFString*>(objectContent.get());
|
||||
}
|
||||
|
||||
const PDFStream* PDFObject::getStream() const
|
||||
{
|
||||
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
|
||||
|
||||
Q_ASSERT(dynamic_cast<const PDFStream*>(objectContent.get()));
|
||||
return static_cast<const PDFStream*>(objectContent.get());
|
||||
}
|
||||
|
||||
const PDFArray* PDFObject::getArray() const
|
||||
{
|
||||
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
|
||||
|
||||
Q_ASSERT(dynamic_cast<const PDFArray*>(objectContent.get()));
|
||||
return static_cast<const PDFArray*>(objectContent.get());
|
||||
}
|
||||
|
||||
bool PDFObject::operator==(const PDFObject &other) const
|
||||
{
|
||||
if (m_type == other.m_type)
|
||||
|
@ -61,6 +86,55 @@ bool PDFObject::operator==(const PDFObject &other) const
|
|||
return false;
|
||||
}
|
||||
|
||||
void PDFObject::accept(PDFAbstractVisitor* visitor) const
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::Null:
|
||||
visitor->visitNull();
|
||||
break;
|
||||
|
||||
case Type::Bool:
|
||||
visitor->visitBool(getBool());
|
||||
break;
|
||||
|
||||
case Type::Int:
|
||||
visitor->visitInt(getInteger());
|
||||
break;
|
||||
|
||||
case Type::Real:
|
||||
visitor->visitReal(getReal());
|
||||
break;
|
||||
|
||||
case Type::String:
|
||||
visitor->visitString(getStringObject());
|
||||
break;
|
||||
|
||||
case Type::Name:
|
||||
visitor->visitName(getStringObject());
|
||||
break;
|
||||
|
||||
case Type::Array:
|
||||
visitor->visitArray(getArray());
|
||||
break;
|
||||
|
||||
case Type::Dictionary:
|
||||
visitor->visitDictionary(getDictionary());
|
||||
break;
|
||||
|
||||
case Type::Stream:
|
||||
visitor->visitStream(getStream());
|
||||
break;
|
||||
|
||||
case Type::Reference:
|
||||
visitor->visitReference(getReference());
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool PDFString::equals(const PDFObjectContent* other) const
|
||||
{
|
||||
Q_ASSERT(dynamic_cast<const PDFString*>(other));
|
||||
|
@ -68,11 +142,6 @@ bool PDFString::equals(const PDFObjectContent* other) const
|
|||
return m_string == otherString->m_string;
|
||||
}
|
||||
|
||||
QByteArray PDFString::getString() const
|
||||
{
|
||||
return m_string;
|
||||
}
|
||||
|
||||
void PDFString::setString(const QByteArray& string)
|
||||
{
|
||||
m_string = string;
|
||||
|
|
|
@ -26,10 +26,15 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFArray;
|
||||
class PDFString;
|
||||
class PDFStream;
|
||||
class PDFDictionary;
|
||||
class PDFAbstractVisitor;
|
||||
|
||||
/// This class represents a content of the PDF object. It can be
|
||||
/// array of objects, dictionary, content stream data, or string data.
|
||||
|
@ -64,6 +69,8 @@ public:
|
|||
Reference
|
||||
};
|
||||
|
||||
static std::vector<Type> getTypes() { return { Type::Null, Type::Bool, Type::Int, Type::Real, Type::String, Type::Name, Type::Array, Type::Dictionary, Type::Stream, Type::Reference }; }
|
||||
|
||||
typedef std::shared_ptr<PDFObjectContent> PDFObjectContentPointer;
|
||||
|
||||
// Default constructor should be constexpr
|
||||
|
@ -97,14 +104,22 @@ public:
|
|||
inline bool isStream() const { return m_type == Type::Stream; }
|
||||
inline bool isReference() const { return m_type == Type::Reference; }
|
||||
|
||||
inline bool getBool() const { return std::get<bool>(m_data); }
|
||||
inline PDFInteger getInteger() const { return std::get<PDFInteger>(m_data); }
|
||||
inline PDFReal getReal() const { return std::get<PDFReal>(m_data); }
|
||||
QByteArray getString() const;
|
||||
const PDFDictionary* getDictionary() const;
|
||||
PDFObjectReference getReference() const { return std::get<PDFObjectReference>(m_data); }
|
||||
const PDFString* getStringObject() const;
|
||||
const PDFStream* getStream() const;
|
||||
const PDFArray* getArray() const;
|
||||
|
||||
bool operator==(const PDFObject& other) const;
|
||||
bool operator!=(const PDFObject& other) const { return !(*this == other); }
|
||||
|
||||
/// Accepts the visitor
|
||||
void accept(PDFAbstractVisitor* visitor) const;
|
||||
|
||||
/// Creates a null object
|
||||
static inline PDFObject createNull() { return PDFObject(); }
|
||||
|
||||
|
@ -165,7 +180,7 @@ public:
|
|||
|
||||
virtual bool equals(const PDFObjectContent* other) const override;
|
||||
|
||||
QByteArray getString() const;
|
||||
const QByteArray& getString() const { return m_string; }
|
||||
void setString(const QByteArray &getString);
|
||||
|
||||
private:
|
||||
|
@ -188,6 +203,9 @@ public:
|
|||
/// Returns size of the array (number of elements)
|
||||
size_t getCount() const { return m_objects.size(); }
|
||||
|
||||
/// Returns capacity of the array (theoretical number of elements before reallocation)
|
||||
size_t getCapacity() const { return m_objects.capacity(); }
|
||||
|
||||
/// Appends object to the end of object list
|
||||
void appendItem(PDFObject object);
|
||||
|
||||
|
@ -233,6 +251,20 @@ public:
|
|||
/// \param value Value
|
||||
void addEntry(QByteArray&& key, PDFObject&& value) { m_dictionary.emplace_back(std::move(key), std::move(value)); }
|
||||
|
||||
/// Returns count of items in the dictionary
|
||||
size_t getCount() const { return m_dictionary.size(); }
|
||||
|
||||
/// Returns capacity of items in the dictionary
|
||||
size_t getCapacity() const { return m_dictionary.capacity(); }
|
||||
|
||||
/// Returns n-th key of the dictionary
|
||||
/// \param index Zero-based index of key in the dictionary
|
||||
const QByteArray& getKey(size_t index) const { return m_dictionary[index].first; }
|
||||
|
||||
/// Returns n-th value of the dictionary
|
||||
/// \param index Zero-based index of value in the dictionary
|
||||
const PDFObject& getValue(size_t index) const { return m_dictionary[index].second; }
|
||||
|
||||
private:
|
||||
/// Finds an item in the dictionary array, if the item is not in the dictionary,
|
||||
/// then end iterator is returned.
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
// Copyright (C) 2018 Jakub Melka
|
||||
//
|
||||
// This file is part of PdfForQt.
|
||||
//
|
||||
// PdfForQt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// PdfForQt 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include "pdfvisitor.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
void PDFAbstractVisitor::acceptArray(const PDFArray* array)
|
||||
{
|
||||
Q_ASSERT(array);
|
||||
|
||||
for (size_t i = 0, count = array->getCount(); i < count; ++i)
|
||||
{
|
||||
array->getItem(i).accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAbstractVisitor::acceptDictionary(const PDFDictionary* dictionary)
|
||||
{
|
||||
Q_ASSERT(dictionary);
|
||||
|
||||
for (size_t i = 0, count = dictionary->getCount(); i < count; ++i)
|
||||
{
|
||||
dictionary->getValue(i).accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAbstractVisitor::acceptStream(const PDFStream* stream)
|
||||
{
|
||||
Q_ASSERT(stream);
|
||||
|
||||
acceptDictionary(stream->getDictionary());
|
||||
}
|
||||
|
||||
PDFStatisticsCollector::PDFStatisticsCollector()
|
||||
{
|
||||
// We must avoid to allocate map item
|
||||
//std::vector<PDFObject::Type> types = PDFObject::getTypes();
|
||||
for (PDFObject::Type type : PDFObject::getTypes())
|
||||
{
|
||||
m_statistics.emplace(std::make_pair(type, Statistics()));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitNull()
|
||||
{
|
||||
collectStatisticsOfSimpleObject(PDFObject::Type::Null);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitBool(bool value)
|
||||
{
|
||||
Q_UNUSED(value);
|
||||
collectStatisticsOfSimpleObject(PDFObject::Type::Bool);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitInt(PDFInteger value)
|
||||
{
|
||||
Q_UNUSED(value);
|
||||
collectStatisticsOfSimpleObject(PDFObject::Type::Int);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitReal(PDFReal value)
|
||||
{
|
||||
Q_UNUSED(value);
|
||||
collectStatisticsOfSimpleObject(PDFObject::Type::Real);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitString(const PDFString* string)
|
||||
{
|
||||
Statistics& statistics = m_statistics[PDFObject::Type::String];
|
||||
collectStatisticsOfString(string, statistics);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitName(const PDFString* name)
|
||||
{
|
||||
Statistics& statistics = m_statistics[PDFObject::Type::Name];
|
||||
collectStatisticsOfString(name, statistics);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitArray(const PDFArray* array)
|
||||
{
|
||||
Statistics& statistics = m_statistics[PDFObject::Type::Array];
|
||||
statistics.count += 1;
|
||||
statistics.memoryConsumptionEstimate += sizeof(PDFObject) + sizeof(PDFArray);
|
||||
|
||||
// We process elements of the array, together with memory consumption,
|
||||
// in the call of acceptArray function. No need to calculate memory consumption here.
|
||||
// Just calculate the overhead.
|
||||
statistics.memoryOverheadEstimate += (array->getCapacity() - array->getCount()) * sizeof(PDFObject);
|
||||
|
||||
acceptArray(array);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitDictionary(const PDFDictionary* dictionary)
|
||||
{
|
||||
Statistics& statistics = m_statistics[PDFObject::Type::Dictionary];
|
||||
collectStatisticsOfDictionary(statistics, dictionary);
|
||||
|
||||
acceptDictionary(dictionary);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitStream(const PDFStream* stream)
|
||||
{
|
||||
Statistics& statistics = m_statistics[PDFObject::Type::Stream];
|
||||
collectStatisticsOfDictionary(statistics, stream->getDictionary());
|
||||
|
||||
acceptStream(stream);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::visitReference(const PDFObjectReference reference)
|
||||
{
|
||||
Q_UNUSED(reference);
|
||||
collectStatisticsOfSimpleObject(PDFObject::Type::Reference);
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::collectStatisticsOfDictionary(Statistics& statistics, const PDFDictionary* dictionary)
|
||||
{
|
||||
statistics.count += 1;
|
||||
statistics.memoryConsumptionEstimate += sizeof(PDFObject) + sizeof(PDFDictionary);
|
||||
|
||||
constexpr uint64_t sizeOfItem = sizeof(std::pair<QByteArray, PDFObject>);
|
||||
constexpr uint64_t sizeOfItemWithoutObject = sizeOfItem - sizeof(PDFObject);
|
||||
|
||||
uint64_t consumptionEstimate = sizeOfItemWithoutObject * dictionary->getCount();
|
||||
uint64_t overheadEstimate = sizeOfItem * (dictionary->getCapacity() - dictionary->getCount());
|
||||
|
||||
for (size_t i = 0, count = dictionary->getCount(); i < count; ++i)
|
||||
{
|
||||
const QByteArray& key = dictionary->getKey(i);
|
||||
|
||||
consumptionEstimate += key.size() * sizeof(char);
|
||||
overheadEstimate += (key.capacity() - key.size()) * sizeof(char);
|
||||
}
|
||||
|
||||
statistics.memoryConsumptionEstimate += consumptionEstimate;
|
||||
statistics.memoryOverheadEstimate += overheadEstimate;
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::collectStatisticsOfString(const PDFString* string, Statistics& statistics)
|
||||
{
|
||||
statistics.count += 1;
|
||||
statistics.memoryConsumptionEstimate += sizeof(PDFObject) + sizeof(PDFString);
|
||||
|
||||
const QByteArray& byteArray = string->getString();
|
||||
const uint64_t memoryConsumption = byteArray.size() * sizeof(char);
|
||||
const uint64_t memoryOverhead = (byteArray.capacity() - byteArray.size()) * sizeof(char);
|
||||
|
||||
statistics.memoryConsumptionEstimate += memoryConsumption;
|
||||
statistics.memoryOverheadEstimate += memoryOverhead;
|
||||
}
|
||||
|
||||
void PDFStatisticsCollector::collectStatisticsOfSimpleObject(PDFObject::Type type)
|
||||
{
|
||||
Statistics& statistics = m_statistics[type];
|
||||
statistics.count += 1;
|
||||
statistics.memoryConsumptionEstimate += sizeof(PDFObject);
|
||||
}
|
||||
|
||||
} // namespace pdf
|
|
@ -0,0 +1,203 @@
|
|||
// Copyright (C) 2018 Jakub Melka
|
||||
//
|
||||
// This file is part of PdfForQt.
|
||||
//
|
||||
// PdfForQt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// PdfForQt 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFVISITOR_H
|
||||
#define PDFVISITOR_H
|
||||
|
||||
#include "pdfglobal.h"
|
||||
#include "pdfobject.h"
|
||||
#include "pdfdocument.h"
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <execution>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
/// Abstract visitor, can iterate trough object tree
|
||||
class PDFAbstractVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
/// This enum identifies how the tree of objects should be processed.
|
||||
/// Some visitors allow parallel (concurrent) run, some visitors are not
|
||||
/// thread safe, so they run in single thread. Please select strategy
|
||||
/// of your visitor carefully to avoid race conditions.
|
||||
enum class Strategy
|
||||
{
|
||||
/// Visitor with this strategy allows fully parallel run across multiple threads.
|
||||
/// So single instance of visitor is used and it is assumed, that all functions
|
||||
/// are thread safe. This means full parallelization.
|
||||
Parallel,
|
||||
|
||||
/// Merging paraller strategy. For each thread, instance of visitor is created, and
|
||||
/// run on object tree. Then they are merged by the \p merge function. Strategy assumes,
|
||||
/// that \p merge function is not reentrant, and it is not run by multiple threads
|
||||
/// on same object. Locking is not needed in this function.
|
||||
Merging,
|
||||
|
||||
/// Do not allow parallel run, use single instance and single thread run. This can
|
||||
/// be slower than previous two strategies, but allows visitor not to be thread
|
||||
/// safe. Use this strategy on visitors, which can't be thread safe from principle.
|
||||
Sequential
|
||||
};
|
||||
|
||||
explicit constexpr PDFAbstractVisitor() = default;
|
||||
virtual ~PDFAbstractVisitor() = default;
|
||||
|
||||
virtual void visitNull() { }
|
||||
virtual void visitBool(bool value) { Q_UNUSED(value); }
|
||||
virtual void visitInt(PDFInteger value) { Q_UNUSED(value); }
|
||||
virtual void visitReal(PDFReal value) { Q_UNUSED(value); }
|
||||
virtual void visitString(const PDFString* string) { Q_UNUSED(string); }
|
||||
virtual void visitName(const PDFString* name) { Q_UNUSED(name); }
|
||||
virtual void visitArray(const PDFArray* array) { Q_UNUSED(array); }
|
||||
virtual void visitDictionary(const PDFDictionary* dictionary) { Q_UNUSED(dictionary); }
|
||||
virtual void visitStream(const PDFStream* stream) { Q_UNUSED(stream); }
|
||||
virtual void visitReference(const PDFObjectReference reference) { Q_UNUSED(reference); }
|
||||
|
||||
protected:
|
||||
void acceptArray(const PDFArray* array);
|
||||
void acceptDictionary(const PDFDictionary* dictionary);
|
||||
void acceptStream(const PDFStream* stream);
|
||||
};
|
||||
|
||||
/// Statistics visitor, collects statistics about PDF object, can be
|
||||
/// invoked from multiple threads.
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFStatisticsCollector : public PDFAbstractVisitor
|
||||
{
|
||||
public:
|
||||
explicit PDFStatisticsCollector();
|
||||
virtual ~PDFStatisticsCollector() override = default;
|
||||
|
||||
/// We implement this visitor as fully parallel
|
||||
static constexpr const Strategy VisitorStrategy = Strategy::Parallel;
|
||||
|
||||
struct Statistics
|
||||
{
|
||||
explicit constexpr Statistics() = default;
|
||||
|
||||
/// This constructor must not be used while \p other is write-accessed from another thread.
|
||||
/// We use relaxed memory order, because we assume this constructor is used only while
|
||||
/// copying data, not when statistics is collected.
|
||||
/// \param other Object, from which data should be copied
|
||||
inline Statistics(const Statistics& other) :
|
||||
count(other.count.load(std::memory_order_relaxed)),
|
||||
memoryConsumptionEstimate(other.memoryConsumptionEstimate.load(std::memory_order_relaxed)),
|
||||
memoryOverheadEstimate(other.memoryOverheadEstimate.load(std::memory_order_relaxed))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::atomic_uint64_t count = 0;
|
||||
std::atomic_uint64_t memoryConsumptionEstimate = 0;
|
||||
std::atomic_uint64_t memoryOverheadEstimate = 0;
|
||||
};
|
||||
|
||||
virtual void visitNull() override;
|
||||
virtual void visitBool(bool value) override;
|
||||
virtual void visitInt(PDFInteger value) override;
|
||||
virtual void visitReal(PDFReal value) override;
|
||||
virtual void visitString(const PDFString* string) override;
|
||||
virtual void visitName(const PDFString* name) override;
|
||||
virtual void visitArray(const PDFArray* array) override;
|
||||
virtual void visitDictionary(const PDFDictionary* dictionary) override;
|
||||
virtual void visitStream(const PDFStream* stream) override;
|
||||
virtual void visitReference(const PDFObjectReference reference) override;
|
||||
|
||||
private:
|
||||
void collectStatisticsOfSimpleObject(PDFObject::Type type);
|
||||
void collectStatisticsOfString(const PDFString* string, Statistics& statistics);
|
||||
void collectStatisticsOfDictionary(Statistics& statistics, const PDFDictionary* dictionary);
|
||||
|
||||
std::map<PDFObject::Type, Statistics> m_statistics;
|
||||
};
|
||||
|
||||
template<typename Visitor, PDFAbstractVisitor::Strategy strategy>
|
||||
struct PDFApplyVisitorImpl;
|
||||
|
||||
template<typename Visitor>
|
||||
void PDFApplyVisitor(const PDFDocument& document, Visitor* visitor)
|
||||
{
|
||||
PDFApplyVisitorImpl<Visitor, Visitor::VisitorStrategy>::apply(document, visitor);
|
||||
}
|
||||
|
||||
template<typename Visitor, PDFAbstractVisitor::Strategy strategy>
|
||||
struct PDFApplyVisitorImpl
|
||||
{
|
||||
// Do nothing, no strategy defined
|
||||
};
|
||||
|
||||
template<typename Visitor>
|
||||
struct PDFApplyVisitorImpl<Visitor, PDFAbstractVisitor::Strategy::Parallel>
|
||||
{
|
||||
static void apply(const PDFDocument& document, Visitor* visitor)
|
||||
{
|
||||
const PDFObjectStorage& storage = document.getStorage();
|
||||
const PDFObjectStorage::PDFObjects& objects = storage.getObjects();
|
||||
const PDFObject& trailerDictionary = storage.getTrailerDictionary();
|
||||
|
||||
std::for_each(std::execution::parallel_policy(), objects.cbegin(), objects.cend(), [visitor](const PDFObjectStorage::Entry& entry) { entry.object.accept(visitor); });
|
||||
trailerDictionary.accept(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Visitor>
|
||||
struct PDFApplyVisitorImpl<Visitor, PDFAbstractVisitor::Strategy::Merging>
|
||||
{
|
||||
static void apply(const PDFDocument& document, Visitor* visitor)
|
||||
{
|
||||
const PDFObjectStorage& storage = document.getStorage();
|
||||
const PDFObjectStorage::PDFObjects& objects = storage.getObjects();
|
||||
const PDFObject& trailerDictionary = storage.getTrailerDictionary();
|
||||
|
||||
QMutex mutex;
|
||||
|
||||
auto process = [&mutex, visitor](const PDFObjectStorage::Entry& entry)
|
||||
{
|
||||
Visitor localVisitor;
|
||||
entry.object.accept(&localVisitor);
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
visitor->merge(&localVisitor);
|
||||
};
|
||||
|
||||
std::for_each(std::execution::parallel_policy(), objects.cbegin(), objects.cend(), process);
|
||||
trailerDictionary.accept(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Visitor>
|
||||
struct PDFApplyVisitorImpl<Visitor, PDFAbstractVisitor::Strategy::Sequential>
|
||||
{
|
||||
static void apply(const PDFDocument& document, Visitor* visitor)
|
||||
{
|
||||
const PDFObjectStorage& storage = document.getStorage();
|
||||
const PDFObjectStorage::PDFObjects& objects = storage.getObjects();
|
||||
const PDFObject& trailerDictionary = storage.getTrailerDictionary();
|
||||
|
||||
std::for_each(std::execution::sequenced_policy(), objects.cbegin(), objects.cend(), [](const PDFObjectStorage::Entry& entry) { entry.object.accept(visitor); });
|
||||
trailerDictionary.accept(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFVISITOR_H
|
|
@ -77,6 +77,9 @@ public:
|
|||
/// then free entry is returned.
|
||||
const Entry& getEntry(PDFObjectReference reference) const;
|
||||
|
||||
/// Returns the trailer dictionary
|
||||
const PDFObject& getTrailerDictionary() const { return m_trailerDictionary; }
|
||||
|
||||
private:
|
||||
/// Reference table entries
|
||||
std::vector<Entry> m_entries;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "ui_pdfviewermainwindow.h"
|
||||
|
||||
#include "pdfdocumentreader.h"
|
||||
#include "pdfvisitor.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
@ -30,6 +31,8 @@ void PDFViewerMainWindow::onActionOpenTriggered()
|
|||
{
|
||||
pdf::PDFDocumentReader reader;
|
||||
pdf::PDFDocument document = reader.readFromFile(fileName);
|
||||
pdf::PDFStatisticsCollector collector;
|
||||
pdf::PDFApplyVisitor(document, &collector);
|
||||
|
||||
if (reader.isSuccessfull())
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue