Advertisement
VRonin

MemoryMappedDevice

Jun 20th, 2016
178
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*******************************************************************************\
  2. *                                                                               *
  3. * This code is free software : you can redistribute it and / or modify          *
  4. * it under the terms of the GNU Lesser General Public License as published by   *
  5. * the Free Software Foundation, either version 3 of the License, or             *
  6. * (at your option) any later version.                                           *
  7. *                                                                               *
  8. * This code is distributed in the hope that it will be useful,                  *
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the                   *
  11. * GNU Lesser General Public License for more details.                           *
  12. *                                                                               *
  13. * A copy of the GNU Lesser General Public License is                            *
  14. * available at < http://www.gnu.org/licenses/ >.                                *
  15. *                                                                               *
  16. \*******************************************************************************/
  17.  
  18. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  19. // MemoryMappedDevice.h
  20. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  21. #ifndef MemoryMappedDevice_h__
  22. #define MemoryMappedDevice_h__
  23. #include <tuple>
  24. #include <QByteArray>
  25. #include <QDataStream>
  26. class QIODevice;
  27. class MemoryMappedDevicePrivate;
  28. /*!
  29. \brief A memory mapped device storing items as key-value pairs
  30. \details This class handles reading and writing of items into a memory mapped file using the key-value pair paradigm.
  31. Items are added to the map with setValue() and retrieved with value().
  32. The type of items saved does not need to be unique but this class will not check for type safety when retrieving values, it has to be done by the user.
  33. To save items of type MyClass in the map tou have to reimplement the operators
  34. \code
  35. QDataStream& operator<<(QDataStream&, const MyClass&);
  36. QDataStream& operator>>(QDataStream& , MyClass&);
  37. \endcode
  38. If the supplied device is not seekable a failed assertion will trigger
  39. */
  40. class MemoryMappedDevice
  41. {
  42.     Q_DECLARE_PRIVATE(MemoryMappedDevice)
  43.     MemoryMappedDevice(MemoryMappedDevicePrivate *d);
  44. public:
  45.     //! Creates an null object
  46.     MemoryMappedDevice();
  47.     virtual ~MemoryMappedDevice()=default;
  48.     //! Creates memory map on the source device
  49.     MemoryMappedDevice(QIODevice* source);
  50.     //! Clears the map and reset it to the source device
  51.     void setDevice(QIODevice* source = nullptr);
  52.     //! Adds the value val to the map associating the given key. Returns true if succesful
  53.     template <class T> bool setValue(qint32 key, const T& val){
  54.         QByteArray block;
  55.         {
  56.             QDataStream writerStream(&block, QIODevice::WriteOnly);
  57.             writerStream << val;
  58.         }
  59.         return writeBlock(key, block);
  60.     }
  61.     //! Retrieves the item associated with the given key. The first element of the tuple determines if the process was succesful
  62.     template <class T> std::tuple<bool,T> value(qint32 key) const {
  63.         static_assert(std::is_default_constructible<T>::value, "T must provide a default constructor");
  64.         T result;
  65.         QByteArray block = readBlock(key);
  66.         if (block.isEmpty())
  67.             return std::make_tuple(false, result);
  68.         QDataStream readerStream(block);
  69.         readerStream >> result;
  70.         return std::make_tuple(true,result);
  71.     }
  72.     //! Clears the map
  73.     void clear();
  74.     //! Removes the item associated with the given key from the map
  75.     bool removeValue(qint32 key);
  76.     //! Returns all the keys of the map
  77.     QList<qint32> keys() const;
  78.     //! Returns true if the map contains an item associated with the given key
  79.     bool contains(qint32 key) const;
  80.     //! Returns the number of items in the map
  81.     qint32 size() const;
  82.     //! Returns the total size of memory occupied by the items in the map
  83.     qint64 deviceSize() const;
  84.     //! Returns true if there are no items in the map
  85.     bool isEmpty() const;
  86.     //! Retunrs the device associated with the map
  87.     QIODevice* device();
  88.     //! Reads the entire contents of the mapped device
  89.     QByteArray readAll() const;
  90. protected:
  91.     MemoryMappedDevice(const MemoryMappedDevice&) = delete;
  92.     MemoryMappedDevice& operator=(const MemoryMappedDevice&) = delete;
  93.     MemoryMappedDevicePrivate* d_ptr;
  94.     bool writeBlock(qint32 key, const QByteArray& block);
  95.     QByteArray readBlock(qint32 key) const;
  96.     qint64 writeInMap(const QByteArray& block);
  97.  
  98. };
  99. #endif // MemoryMappedDevice_h__
  100. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  101. // MemoryMappedDevice_p.h
  102. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  103. #ifndef MemoryMappedDevice_p_h__
  104. #define MemoryMappedDevice_p_h__
  105.  
  106. #include <QPointer>
  107. #include <QHash>
  108. #include <QMap>
  109. #include "MemoryMappedDevice.h"
  110. class QIODevice;
  111. class MemoryMappedDevicePrivate
  112. {
  113.     Q_DECLARE_PUBLIC(MemoryMappedDevice)
  114. public:
  115.     MemoryMappedDevicePrivate(MemoryMappedDevice *q);
  116.     virtual ~MemoryMappedDevicePrivate();
  117. private:
  118.     MemoryMappedDevicePrivate(const MemoryMappedDevicePrivate &other) =delete;
  119.     MemoryMappedDevicePrivate& operator=(const MemoryMappedDevicePrivate &other) = delete;
  120. protected:
  121.     QPointer<QIODevice> m_device;
  122.     QHash<qint32, qint64> m_itemsMap;
  123.     QMap<qint64, bool> m_memoryMap;
  124.     MemoryMappedDevice* q_ptr;
  125. };
  126.  
  127. #endif // MemoryMappedDevice_p_h__
  128.  
  129. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  130. // MemoryMappedDevice.cpp
  131. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  132. #include "MemoryMappedDevice.h"
  133. #include "MemoryMappedDevice_p.h"
  134. #include <QIODevice>
  135. MemoryMappedDevicePrivate::MemoryMappedDevicePrivate(MemoryMappedDevice *q)
  136.     :q_ptr(q)
  137.     ,m_device(nullptr)
  138. {}
  139. MemoryMappedDevice::MemoryMappedDevice()
  140.     : MemoryMappedDevice(static_cast<QIODevice*>(nullptr))
  141. {}
  142. MemoryMappedDevice::MemoryMappedDevice(MemoryMappedDevicePrivate *d)
  143.     :d_ptr(d)
  144. {
  145.    
  146. }
  147.  
  148. MemoryMappedDevicePrivate::~MemoryMappedDevicePrivate()
  149. {
  150.     if (m_device) {
  151.         if (m_device->isOpen()) {
  152.             m_device->close();
  153.         }
  154.     }
  155. }
  156. MemoryMappedDevice::MemoryMappedDevice(QIODevice* source)
  157.     :MemoryMappedDevice(new MemoryMappedDevicePrivate(this))
  158. {
  159.     setDevice(source);
  160. }
  161.  
  162.  
  163.  
  164. void MemoryMappedDevice::setDevice(QIODevice* source)
  165. {
  166.     Q_D(MemoryMappedDevice);
  167.     if (d->m_device) {
  168.         if (d->m_device->isOpen()) {
  169.             d->m_device->close();
  170.         }
  171.     }
  172.     d->m_itemsMap.clear();
  173.     d->m_memoryMap.clear();
  174.     d->m_memoryMap.insert(0, true);
  175.     d->m_device = source;
  176.     if (d->m_device) {
  177.         if (d->m_device->isSequential()) {
  178.             Q_ASSERT_X(false, "MemoryMappedDevice()", "Device must be seekable");
  179.             d->m_device = nullptr;
  180.         }
  181.         if (!d->m_device->isOpen()) {
  182.             d->m_device->open(QIODevice::ReadWrite);
  183.         }
  184.     }
  185.  
  186. }
  187.  
  188. void MemoryMappedDevice::clear()
  189. {
  190.     Q_D(MemoryMappedDevice);
  191.     if (d->m_device) {
  192.         if (d->m_device->isOpen()) {
  193.             d->m_device->close();
  194.         }
  195.         d->m_device->open(QIODevice::Truncate);
  196.         d->m_device->close();
  197.         d->m_device->open(QIODevice::ReadWrite);
  198.     }
  199.     d->m_itemsMap.clear();
  200.     d->m_memoryMap.clear();
  201.     d->m_memoryMap.insert(0, true);
  202. }
  203.  
  204. bool MemoryMappedDevice::removeValue(qint32 key)
  205. {
  206.     Q_D(MemoryMappedDevice);
  207.     auto itemIter = d->m_itemsMap.find(key);
  208.     if (itemIter == d->m_itemsMap.end())
  209.         return false;
  210.     auto fileIter = d->m_memoryMap.find(itemIter.value());
  211.     Q_ASSERT(fileIter != d->m_memoryMap.end());
  212.     if (fileIter.value())
  213.         return false;
  214.     d->m_itemsMap.erase(itemIter);
  215.     if (fileIter != d->m_memoryMap.begin()) {
  216.         if ((fileIter - 1).value()) {
  217.             fileIter = d->m_memoryMap.erase(fileIter);
  218.             if (fileIter.value())
  219.                 d->m_memoryMap.erase(fileIter);
  220.             return true;
  221.         }
  222.     }
  223.     if ((fileIter + 1) != d->m_memoryMap.end()) {
  224.         if ((fileIter + 1).value())
  225.             d->m_memoryMap.erase(fileIter + 1);
  226.     }
  227.     fileIter.value() = true;
  228.     return true;
  229. }
  230.  
  231. QList<qint32> MemoryMappedDevice::keys() const
  232. {
  233.     Q_D(const MemoryMappedDevice);
  234.     return d->m_itemsMap.keys();
  235. }
  236.  
  237. bool MemoryMappedDevice::contains(qint32 key) const
  238. {
  239.     Q_D(const MemoryMappedDevice);
  240.     return d->m_itemsMap.contains(key);
  241. }
  242.  
  243. qint32 MemoryMappedDevice::size() const
  244. {
  245.     Q_D(const MemoryMappedDevice);
  246.     return d->m_itemsMap.size();
  247. }
  248.  
  249. qint64 MemoryMappedDevice::deviceSize() const
  250. {
  251.     Q_D(const MemoryMappedDevice);
  252.     if (!d->m_device)
  253.         return 0;
  254.     return d->m_device->size();
  255. }
  256.  
  257. bool MemoryMappedDevice::isEmpty() const
  258. {
  259.     Q_D(const MemoryMappedDevice);
  260.     return d->m_itemsMap.isEmpty();
  261. }
  262.  
  263. QIODevice* MemoryMappedDevice::device()
  264. {
  265.     Q_D(MemoryMappedDevice);
  266.     return d->m_device;
  267. }
  268.  
  269. QByteArray MemoryMappedDevice::readAll() const
  270. {
  271.     Q_D(MemoryMappedDevice);
  272.     if (!d->m_device)
  273.         return QByteArray();
  274.     d->m_device->seek(0);
  275.     return d->m_device->readAll();
  276. }
  277.  
  278. bool MemoryMappedDevice::writeBlock(qint32 key, const QByteArray& block)
  279. {
  280.     Q_D(MemoryMappedDevice);
  281.     if (!d->m_device)
  282.         return false;
  283.     if (!d->m_device->isOpen()) {
  284.         if (!d->m_device->open(QIODevice::ReadWrite))
  285.             return false;
  286.     }
  287.     if (!d->m_device->isWritable())
  288.         return false;
  289.     d->m_device->setTextModeEnabled(false);
  290.     auto itemIter = d->m_itemsMap.find(key);
  291.     if (itemIter != d->m_itemsMap.end()) {
  292.         removeValue(key);
  293.     }
  294.     d->m_itemsMap.insert(key, writeInMap(block));
  295.     return true;
  296. }
  297.  
  298. QByteArray MemoryMappedDevice::readBlock(qint32 key) const
  299. {
  300.     Q_D(const MemoryMappedDevice);
  301.     if (!d->m_device)
  302.         return QByteArray();
  303.     if (!d->m_device->isOpen()) {
  304.         if (!d->m_device->open(QIODevice::ReadWrite))
  305.             return QByteArray();
  306.     }
  307.     if (!d->m_device->isReadable())
  308.         return QByteArray();
  309.     d->m_device->setTextModeEnabled(false);
  310.     auto itemIter = d->m_itemsMap.constFind(key);
  311.     if (itemIter == d->m_itemsMap.constEnd())
  312.         return QByteArray();
  313.     auto fileIter = d->m_memoryMap.constFind(itemIter.value());
  314.     Q_ASSERT(fileIter != d->m_memoryMap.constEnd());
  315.     if(fileIter.value())
  316.         return QByteArray();
  317.     auto nextIter = fileIter + 1;
  318.     d->m_device->seek(fileIter.key());
  319.     if (nextIter == d->m_memoryMap.constEnd())
  320.         return d->m_device->readAll();
  321.     return d->m_device->read(nextIter.key() - fileIter.key());
  322. }
  323.  
  324. qint64 MemoryMappedDevice::writeInMap(const QByteArray& block)
  325. {
  326.     Q_D(MemoryMappedDevice);
  327.     for (auto i = d->m_memoryMap.begin(); i != d->m_memoryMap.end(); ++i) {
  328.         if (i.value()) {
  329.             // Space is available
  330.             auto j = i + 1;
  331.             if (j != d->m_memoryMap.end()) {
  332.                 if (i.key() + static_cast<qint64>(block.size()) > j.key())
  333.                     // Not enough space to save here
  334.                     continue;
  335.                 if (i.key() + static_cast<qint64>(block.size()) < j.key()) {
  336.                     // Item smaller than available space
  337.                     d->m_memoryMap.insert(i.key() + static_cast<qint64>(block.size()), true);
  338.                 }
  339.             }
  340.             else {
  341.                 d->m_memoryMap.insert(i.key() + block.size(), true);
  342.             }
  343.             i.value() = false;
  344.             d->m_device->seek(i.key());
  345.             d->m_device->write(block);
  346.             return i.key();
  347.         }
  348.     }
  349.     Q_UNREACHABLE();
  350.     return 0;
  351. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement