File indexing completed on 2024-12-22 04:10:29

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_debug.h"
0008 #include "kis_memory_window.h"
0009 
0010 #include <QDir>
0011 
0012 #define SWP_PREFIX "KRITA_SWAP_FILE_XXXXXX"
0013 
0014 KisMemoryWindow::KisMemoryWindow(const QString &swapDir, quint64 writeWindowSize)
0015     : m_readWindowEx(writeWindowSize / 4),
0016       m_writeWindowEx(writeWindowSize)
0017 {
0018     m_valid = true;
0019 
0020     // swapDir will never be empty, as KisImageConfig::swapDir() always provides
0021     // us with a (platform specific) default directory, even if none is explicitly
0022     // configured by the user; also we do not want any logic that determines the
0023     // default swap dir here.
0024     KIS_SAFE_ASSERT_RECOVER_NOOP(!swapDir.isEmpty());
0025 
0026     QDir d(swapDir);
0027     if (!d.exists()) {
0028         m_valid = d.mkpath(swapDir);
0029     }
0030 
0031     const QString swapFileTemplate = swapDir + '/' + SWP_PREFIX;
0032 
0033     if (m_valid) {
0034         m_file.setFileTemplate(swapFileTemplate);
0035         bool res = m_file.open();
0036         if (!res || m_file.fileName().isEmpty()) {
0037             m_valid = false;
0038         }
0039     }
0040 
0041     if (!m_valid) {
0042         qWarning() << "Could not create or open swapfile; disabling swapfile" << swapFileTemplate;
0043     }
0044 }
0045 
0046 KisMemoryWindow::~KisMemoryWindow()
0047 {
0048 }
0049 
0050 quint8* KisMemoryWindow::getReadChunkPtr(const KisChunkData &readChunk)
0051 {
0052     if (!adjustWindow(readChunk, &m_readWindowEx, &m_writeWindowEx)) {
0053         return nullptr;
0054     }
0055 
0056     return m_readWindowEx.calculatePointer(readChunk);
0057 }
0058 
0059 quint8* KisMemoryWindow::getWriteChunkPtr(const KisChunkData &writeChunk)
0060 {
0061     if (!adjustWindow(writeChunk, &m_writeWindowEx, &m_readWindowEx)) {
0062         return nullptr;
0063     }
0064 
0065     return m_writeWindowEx.calculatePointer(writeChunk);
0066 }
0067 
0068 bool KisMemoryWindow::adjustWindow(const KisChunkData &requestedChunk,
0069                                    MappingWindow *adjustingWindow,
0070                                    MappingWindow *otherWindow)
0071 {
0072     if(!(adjustingWindow->window) ||
0073        !(requestedChunk.m_begin >= adjustingWindow->chunk.m_begin &&
0074          requestedChunk.m_end <= adjustingWindow->chunk.m_end))
0075     {
0076         m_file.unmap(adjustingWindow->window);
0077 
0078         quint64 windowSize = adjustingWindow->defaultSize;
0079         if(requestedChunk.size() > windowSize) {
0080             warnKrita <<
0081                 "KisMemoryWindow: the requested chunk is too "
0082                 "big to fit into the mapping! "
0083                 "Adjusting mapping to avoid SIGSEGV...";
0084 
0085             windowSize = requestedChunk.size();
0086         }
0087 
0088         adjustingWindow->chunk.setChunk(requestedChunk.m_begin, windowSize);
0089 
0090         if(adjustingWindow->chunk.m_end >= (quint64)m_file.size()) {
0091             // Align by 32 bytes
0092             quint64 newSize = (adjustingWindow->chunk.m_end + 1 + 32) & (~31ULL);
0093 
0094 #ifdef Q_OS_WIN32
0095             /**
0096              * Workaround for Qt's "feature"
0097              *
0098              * On windows QFSEnginePrivate caches the value of
0099              * mapHandle which is limited to the size of the file at
0100              * the moment of its (handle's) creation. That is we will
0101              * not be able to use it after resizing the file.  The
0102              * only way to free the handle is to release all the
0103              * mappings we have. Sad but true.
0104              */
0105             if (otherWindow->chunk.size()) {
0106                 m_file.unmap(otherWindow->window);
0107             }
0108 #else
0109             Q_UNUSED(otherWindow);
0110 #endif
0111 
0112             if (!m_file.resize(newSize)) {
0113                 return false;
0114             }
0115 
0116 #ifdef Q_OS_WIN32
0117             if (otherWindow->chunk.size()) {
0118                 otherWindow->window = m_file.map(otherWindow->chunk.m_begin,
0119                                                  otherWindow->chunk.size());
0120             }
0121 #endif
0122         }
0123 
0124 #ifdef Q_OS_UNIX
0125         // A workaround for https://bugreports.qt-project.org/browse/QTBUG-6330
0126         m_file.exists();
0127 #endif
0128 
0129         adjustingWindow->window = m_file.map(adjustingWindow->chunk.m_begin,
0130                                              adjustingWindow->chunk.size());
0131 
0132         if (!adjustingWindow->window) {
0133             return false;
0134         }
0135     }
0136 
0137     return true;
0138 }