kwave  18.07.70
SwapFile.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  SwapFile.cpp - Provides virtual memory in a swap file
3  -------------------
4  begin : Fri Aug 17 2001
5  copyright : (C) 2000 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <limits.h>
21 #include <unistd.h> // for unlink() and getpagesize()
22 
23 #include "libkwave/String.h"
24 #include "libkwave/SwapFile.h"
25 #include "libkwave/Utils.h"
26 
27 // just for debugging: number of open swapfiles
28 static unsigned int g_instances = 0;
29 
31 #define MINIMUM_SIZE (16 << 20)
32 
34 #define BLOCK_SIZE (4 << 20)
35 
36 //***************************************************************************
38  :m_file(name), m_address(Q_NULLPTR), m_size(0), m_pagesize(0), m_map_count(0)
39 {
40  // determine the system's native page size
41 #if defined(HAVE_GETPAGESIZE)
42  if (!m_pagesize) m_pagesize = getpagesize();
43 #endif
44 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
45  if (!m_pagesize) m_pagesize = sysconf(_SC_PAGESIZE);
46 #endif
47 
48  // fallback: assume 4kB pagesize
49  if (Kwave::toInt(m_pagesize) <= 0) {
50  qWarning("SwapFile: unable to determine page size, using fallback");
51  m_pagesize = (4 << 10);
52  }
53 
54  g_instances++;
55 }
56 
57 //***************************************************************************
59 {
60  close();
61  g_instances--;
62 }
63 
64 //***************************************************************************
66 {
67  Q_ASSERT(!m_address); // MUST NOT be mapped !
68  if (m_address) return false;
69 
70  if (m_size) close();
71 // qDebug("SwapFile::allocate(%u), instances: %u",
72 // Kwave::toUint(size), g_instances);
73 
74  // try to create the temporary file
75  if (!m_file.open()) {
76  qWarning("SwapFile(%s) -> open failed", DBG(m_file.fileName()));
77  return false;
78  }
79 
80  // when it is created, also try to unlink it so that it will always
81  // be removed, even if the application crashes !
82 #ifdef HAVE_UNLINK
83  unlink(m_file.fileName().toLocal8Bit().data());
84 #endif /* HAVE_UNLINK */
85 
86  // round up the new size to a full page
87  size_t rounded = Kwave::round_up<size_t>(size, m_pagesize);
88 
89  // touch each new page in order to *really* allocate the disk space
90  size_t offset = 0;
91  while (offset < rounded) {
92  // qDebug("SwapFile: touching at offset 0x%08X",
93  // Kwave::toUint(offset));
94  m_file.seek(offset);
95  m_file.putChar(0);
96  m_file.flush();
97  if (m_file.pos() != static_cast<qint64>(offset + 1)) {
98  qWarning("SwapFile::allocate(): seek failed. DISK FULL !?");
99  return false;
100  }
101  offset += m_pagesize;
102  }
103 
104  m_file.seek(rounded - 1);
105  m_file.putChar(0);
106  m_file.flush();
107  if (m_file.pos() + 1 < static_cast<qint64>(size)) {
108  qWarning("SwapFile::allocate(%d MB) failed, DISK FULL ?",
109  Kwave::toUint(size >> 20));
110  m_size = 0;
111  return false;
112  }
113 
114  // now the size is valid
115  m_size = size;
116 
117 // qDebug("SwapFile::allocate(%d kB)",size >> 10);
118  return true;
119 }
120 
121 //***************************************************************************
123 {
124  Q_ASSERT(!m_address); // MUST NOT be mappped !
125  Q_ASSERT(!m_map_count);
126  if (m_address || m_map_count) return false;
127  if (size == m_size) return true; // nothing to do
128 
129  // special case: shutdown
130  if (size == 0) {
131  close();
132  return true;
133  }
134 
135  // this file seems to be a growing one:
136  // round up the new size to a full block
137  size_t rounded = Kwave::round_up<size_t>(size, BLOCK_SIZE);
138 
139  // optimization: if rounded size already matches -> done
140  if (rounded == m_size) {
141 // qDebug("SwapFile::resize(%u MB) -> skipped, already big enough",
142 // Kwave::toUint(size >> 20));
143  return true;
144  }
145 
146  // do not shrink below minimum size
147  if ((size < m_size) && (size < MINIMUM_SIZE)) {
148 // qDebug("SwapFile::resize(%u MB) -> skipped, limited by min size",
149 // Kwave::toUint(size >> 20));
150  return true;
151  }
152 
153  // resize the file
154  // qDebug("SwapFile::resize(%u)", size);
155 
156  // touch each new page in order to *really* allocate the disk space
157  size_t offset = static_cast<size_t>((m_size + m_pagesize - 1) / m_pagesize);
158  while (offset < rounded) {
159 // qDebug("SwapFile: touching at offset 0x%08X",
160 // Kwave::toUint(offset));
161  m_file.seek(offset);
162  m_file.putChar(0);
163  m_file.flush();
164  if (m_file.pos() != static_cast<qint64>(offset + 1)) {
165  qWarning("SwapFile::resize(): seek failed. DISK FULL !?");
166  return false;
167  }
168  offset += m_pagesize;
169  }
170 
171  bool ok = true;
172  ok &= m_file.resize(rounded);
173  ok &= m_file.seek(rounded - 1);
174  ok &= m_file.putChar(0);
175  ok &= m_file.flush();
176  if (ok) {
177  m_size = rounded;
178  } else {
179  qWarning("SwapFile::resize(): failed. DISK FULL !?");
180  }
181 
182 // qDebug("SwapFile::resize() to size=%u", m_size);
183  return ok;
184 }
185 
186 //***************************************************************************
188 {
189  Q_ASSERT(!m_map_count);
190  if (m_address) m_file.unmap(static_cast<uchar *>(m_address));
191  m_address = Q_NULLPTR;
192  m_size = 0;
193 
194  m_file.resize(0);
195  if (m_file.isOpen()) m_file.close();
196 
197  if (m_file.exists(m_file.fileName())) {
198  if (!m_file.remove()) {
199  qWarning("SwapFile(%s) -> remove FAILED", DBG(m_file.fileName()));
200  }
201  }
202 
203 }
204 
205 //***************************************************************************
207 {
208 // qDebug(" SwapFile::map() - m_size=%u", m_size);
209 
210  // shortcut if already mapped
211  if (m_map_count) {
212  m_map_count++;
213  Q_ASSERT(m_address);
214  return m_address;
215  }
216 
217  m_file.flush();
218 
219  m_address = m_file.map(0, m_size, QFile::NoOptions);
220 
221  // map -1 to null pointer
222  if (m_address == reinterpret_cast<void *>(-1)) m_address = Q_NULLPTR;
223 
224  // if succeeded, increase map reference counter
225  if (m_address) {
226  m_map_count++;
227  } else {
228  qWarning("SwapFile(%s) -> map FAILED", DBG(m_file.fileName()));
229  }
230 
231  return m_address;
232 }
233 
234 //***************************************************************************
236 {
237  Q_ASSERT(m_address);
238  Q_ASSERT(m_size);
239  Q_ASSERT(m_map_count);
240 
241  // first decrease refcount and do nothing if not zero
242  if (!m_map_count) return 0;
243  if (--m_map_count) return m_map_count;
244 
245  // really do the unmap
246  if (m_size && m_address) {
247 // qDebug(" --- SwapFile::unmap() (%p)", this);
248  if (!m_file.unmap(static_cast<uchar *>(m_address))) {
249  qWarning("SwapFile(%s) -> unmap FAILED", DBG(m_file.fileName()));
250  }
251  }
252 
253  m_address = Q_NULLPTR;
254  return m_map_count;
255 }
256 
257 //***************************************************************************
258 int Kwave::SwapFile::read(unsigned int offset, void *buffer,
259  unsigned int length)
260 {
261  // seek to the given offset
262  if (!m_file.seek(offset)) return -1;
263 
264  // read into the buffer
265  if (!m_file.flush()) return -1;
266 
267  if (length > INT_MAX) length = INT_MAX;
268  return Kwave::toInt(
269  m_file.read(reinterpret_cast<char *>(buffer), length)
270  );
271 }
272 
273 //***************************************************************************
274 int Kwave::SwapFile::write(unsigned int offset, const void *buffer,
275  unsigned int length)
276 {
277  // seek to the given offset
278  if (!m_file.seek(offset)) return -1;
279 
280  // write data from the buffer
281  if (length > INT_MAX) length = INT_MAX;
282  return Kwave::toInt(
283  m_file.write(reinterpret_cast<const char *>(buffer), length)
284  );
285 }
286 
287 //***************************************************************************
288 //***************************************************************************
void * map()
Definition: SwapFile.cpp:206
int write(unsigned int offset, const void *buffer, unsigned int length)
Definition: SwapFile.cpp:274
#define BLOCK_SIZE
Definition: SwapFile.cpp:34
#define MINIMUM_SIZE
Definition: SwapFile.cpp:31
const char name[16]
Definition: memcpy.c:510
size_t m_pagesize
Definition: SwapFile.h:135
int read(unsigned int offset, void *buffer, unsigned int length)
Definition: SwapFile.cpp:258
int toInt(T x)
Definition: Utils.h:127
bool allocate(size_t size)
Definition: SwapFile.cpp:65
size_t size() const
Definition: SwapFile.h:64
size_t m_size
Definition: SwapFile.h:132
SwapFile(const QString &name)
Definition: SwapFile.cpp:37
virtual ~SwapFile()
Definition: SwapFile.cpp:58
#define DBG(qs)
Definition: String.h:55
bool resize(size_t size)
Definition: SwapFile.cpp:122
unsigned int toUint(T x)
Definition: Utils.h:109
void * m_address
Definition: SwapFile.h:129
static unsigned int g_instances
Definition: SwapFile.cpp:28
QTemporaryFile m_file
Definition: SwapFile.h:126