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 }