Mercurial > hg > svcore
comparison base/MatrixFileCache.cpp @ 90:c4e163f911dd
* Switch spectrogram layer over to using the new rudimentary disk-backed
FFT cache
author | Chris Cannam |
---|---|
date | Wed, 03 May 2006 14:26:26 +0000 |
parents | 7de62a884810 |
children | 1dcf41ed3863 |
comparison
equal
deleted
inserted
replaced
89:6a1803d578e0 | 90:c4e163f911dd |
---|---|
13 COPYING included with this distribution for more information. | 13 COPYING included with this distribution for more information. |
14 */ | 14 */ |
15 | 15 |
16 #include "MatrixFileCache.h" | 16 #include "MatrixFileCache.h" |
17 #include "base/TempDirectory.h" | 17 #include "base/TempDirectory.h" |
18 #include "base/System.h" | |
18 | 19 |
19 #include <sys/types.h> | 20 #include <sys/types.h> |
20 #include <sys/stat.h> | 21 #include <sys/stat.h> |
21 #include <fcntl.h> | 22 #include <fcntl.h> |
22 #include <unistd.h> | 23 #include <unistd.h> |
26 #include <cstdio> | 27 #include <cstdio> |
27 | 28 |
28 #include <QFileInfo> | 29 #include <QFileInfo> |
29 #include <QDir> | 30 #include <QDir> |
30 | 31 |
32 #define HAVE_MMAP 1 | |
33 | |
34 #ifdef HAVE_MMAP | |
35 #include <sys/mman.h> | |
36 #endif | |
37 | |
38 //!!! This class is a work in progress -- it does only as much as we | |
39 // need for the current SpectrogramLayer. Slated for substantial | |
40 // refactoring and extension. | |
41 | |
31 MatrixFileCache::MatrixFileCache(QString fileBase, Mode mode) : | 42 MatrixFileCache::MatrixFileCache(QString fileBase, Mode mode) : |
32 m_fd(-1), | 43 m_fd(-1), |
33 m_off(-1), | |
34 m_mode(mode), | 44 m_mode(mode), |
35 m_width(0), | 45 m_width(0), |
36 m_height(0), | 46 m_height(0), |
47 m_headerSize(2 * sizeof(size_t)), | |
48 m_autoRegionWidth(2048), | |
49 m_off(-1), | |
37 m_rx(0), | 50 m_rx(0), |
38 m_rw(0), | 51 m_rw(0), |
39 m_range(0), | 52 m_userRegion(false), |
40 m_headerSize(2 * sizeof(size_t)) | 53 m_region(0), |
41 { | 54 m_mmapped(false), |
55 m_mmapSize(0), | |
56 m_mmapOff(0), | |
57 m_preferMmap(true) | |
58 { | |
59 // Ensure header size is a multiple of the size of our data (for | |
60 // alignment purposes) | |
61 size_t hs = ((m_headerSize / sizeof(float)) * sizeof(float)); | |
62 if (hs != m_headerSize) m_headerSize = hs + sizeof(float); | |
63 | |
42 QDir tempDir(TempDirectory::instance()->getPath()); | 64 QDir tempDir(TempDirectory::instance()->getPath()); |
43 QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase))); | 65 QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase))); |
44 bool newFile = !QFileInfo(fileName).exists(); | 66 bool newFile = !QFileInfo(fileName).exists(); |
45 | 67 |
46 if (newFile && mode == ReadOnly) { | 68 if (newFile && mode == ReadOnly) { |
56 flags = O_RDWR | O_CREAT; | 78 flags = O_RDWR | O_CREAT; |
57 } else { | 79 } else { |
58 flags = O_RDONLY; | 80 flags = O_RDONLY; |
59 } | 81 } |
60 | 82 |
61 if ((m_fd = ::open(fileName.toLocal8Bit(), flags, mode)) < 0) { | 83 if ((m_fd = ::open(fileName.toLocal8Bit(), flags, fmode)) < 0) { |
62 ::perror("Open failed"); | 84 ::perror("Open failed"); |
63 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: " | 85 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: " |
64 << "Failed to open cache file \"" | 86 << "Failed to open cache file \"" |
65 << fileName.toStdString() << "\""; | 87 << fileName.toStdString() << "\""; |
66 if (mode == ReadWrite) std::cerr << " for writing"; | 88 if (mode == ReadWrite) std::cerr << " for writing"; |
69 | 91 |
70 if (newFile) { | 92 if (newFile) { |
71 resize(0, 0); // write header | 93 resize(0, 0); // write header |
72 } else { | 94 } else { |
73 size_t header[2]; | 95 size_t header[2]; |
74 if (::read(m_fd, header, 2 * sizeof(size_t))) { | 96 if (::read(m_fd, header, 2 * sizeof(size_t)) < 0) { |
75 perror("Read failed"); | 97 perror("Read failed"); |
76 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: " | 98 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: " |
77 << "Failed to read header" << std::endl; | 99 << "Failed to read header (fd " << m_fd << ", file \"" |
100 << fileName.toStdString() << "\")" << std::endl; | |
78 return; | 101 return; |
79 } | 102 } |
80 m_width = header[0]; | 103 m_width = header[0]; |
81 m_height = header[1]; | 104 m_height = header[1]; |
82 seekTo(0, 0); | 105 seekTo(0, 0); |
83 } | 106 } |
84 | 107 |
85 std::cerr << "MatrixFileCache::MatrixFileCache: Done, size is " << m_width << "x" << m_height << std::endl; | 108 std::cerr << "MatrixFileCache::MatrixFileCache: Done, size is " << "(" << m_width << ", " << m_height << ")" << std::endl; |
86 | 109 |
87 } | 110 } |
88 | 111 |
89 MatrixFileCache::~MatrixFileCache() | 112 MatrixFileCache::~MatrixFileCache() |
90 { | 113 { |
114 if (m_rw > 0) { | |
115 if (m_mmapped) { | |
116 #ifdef HAVE_MMAP | |
117 ::munmap(m_region, m_mmapSize); | |
118 #endif | |
119 } else { | |
120 delete[] m_region; | |
121 } | |
122 } | |
123 | |
91 if (m_fd >= 0) { | 124 if (m_fd >= 0) { |
92 if (::close(m_fd) < 0) { | 125 if (::close(m_fd) < 0) { |
93 ::perror("MatrixFileCache::~MatrixFileCache: close failed"); | 126 ::perror("MatrixFileCache::~MatrixFileCache: close failed"); |
94 } | 127 } |
95 } | 128 } |
129 | |
130 //!!! refcount and unlink | |
96 } | 131 } |
97 | 132 |
98 size_t | 133 size_t |
99 MatrixFileCache::getWidth() const | 134 MatrixFileCache::getWidth() const |
100 { | 135 { |
118 | 153 |
119 off_t off = m_headerSize + (w * h * sizeof(float)); | 154 off_t off = m_headerSize + (w * h * sizeof(float)); |
120 | 155 |
121 if (w * h > m_width * m_height) { | 156 if (w * h > m_width * m_height) { |
122 | 157 |
123 if (::lseek(m_fd, off - sizeof(float), SEEK_SET) == (off_t)-1) { | 158 #ifdef HAVE_MMAP |
124 ::perror("Seek failed"); | 159 // If we're going to mmap the file, we need to ensure it's long |
125 std::cerr << "ERROR: MatrixFileCache::resize(" << w << ", " | 160 // enough beforehand |
126 << h << "): seek failed, cannot resize" << std::endl; | |
127 return; | |
128 } | |
129 | |
130 // guess this requires efficient support for sparse files | |
131 | 161 |
132 float f(0); | 162 if (m_preferMmap) { |
133 if (::write(m_fd, &f, sizeof(float)) != sizeof(float)) { | 163 |
134 ::perror("WARNING: MatrixFileCache::resize: write failed"); | 164 if (::lseek(m_fd, off - sizeof(float), SEEK_SET) == (off_t)-1) { |
135 } | 165 ::perror("Seek failed"); |
166 std::cerr << "ERROR: MatrixFileCache::resize(" << w << ", " | |
167 << h << "): seek failed, cannot resize" << std::endl; | |
168 return; | |
169 } | |
170 | |
171 // guess this requires efficient support for sparse files | |
172 | |
173 float f(0); | |
174 if (::write(m_fd, &f, sizeof(float)) != sizeof(float)) { | |
175 ::perror("WARNING: MatrixFileCache::resize: write failed"); | |
176 } | |
177 } | |
178 #endif | |
136 | 179 |
137 } else { | 180 } else { |
138 | 181 |
139 if (::ftruncate(m_fd, off) < 0) { | 182 if (::ftruncate(m_fd, off) < 0) { |
140 ::perror("MatrixFileCache::resize: ftruncate failed"); | 183 ::perror("WARNING: MatrixFileCache::resize: ftruncate failed"); |
141 } | 184 } |
142 } | 185 } |
143 | 186 |
144 m_width = 0; | 187 m_width = 0; |
145 m_height = 0; | 188 m_height = 0; |
171 std::cerr << "ERROR: MatrixFileCache::reset called on read-only cache" | 214 std::cerr << "ERROR: MatrixFileCache::reset called on read-only cache" |
172 << std::endl; | 215 << std::endl; |
173 return; | 216 return; |
174 } | 217 } |
175 | 218 |
176 //... | 219 float *emptyCol = new float[m_height]; |
177 } | 220 for (size_t y = 0; y < m_height; ++y) emptyCol[y] = 0.f; |
178 | 221 |
179 void | 222 seekTo(0, 0); |
180 MatrixFileCache::setRangeOfInterest(size_t x, size_t width) | 223 for (size_t x = 0; x < m_width; ++x) setColumnAt(x, emptyCol); |
181 { | 224 |
225 delete[] emptyCol; | |
226 } | |
227 | |
228 void | |
229 MatrixFileCache::setRegionOfInterest(size_t x, size_t width) | |
230 { | |
231 setRegion(x, width, true); | |
232 } | |
233 | |
234 void | |
235 MatrixFileCache::clearRegionOfInterest() | |
236 { | |
237 m_userRegion = false; | |
182 } | 238 } |
183 | 239 |
184 float | 240 float |
185 MatrixFileCache::getValueAt(size_t x, size_t y) const | 241 MatrixFileCache::getValueAt(size_t x, size_t y) const |
186 { | 242 { |
187 if (m_rw > 0 && x >= m_rx && x < m_rx + m_rw) { | 243 if (m_rw > 0 && x >= m_rx && x < m_rx + m_rw) { |
188 return m_range[x - m_rx][y]; | 244 float *rp = getRegionPtr(x, y); |
245 if (rp) return *rp; | |
246 } else if (!m_userRegion) { | |
247 if (autoSetRegion(x)) { | |
248 float *rp = getRegionPtr(x, y); | |
249 if (rp) return *rp; | |
250 } | |
189 } | 251 } |
190 | 252 |
191 if (!seekTo(x, y)) return 0.f; | 253 if (!seekTo(x, y)) return 0.f; |
192 float value; | 254 float value; |
193 if (::read(m_fd, &value, sizeof(float)) != sizeof(float)) { | 255 ssize_t r = ::read(m_fd, &value, sizeof(float)); |
256 if (r != sizeof(float)) { | |
194 ::perror("MatrixFileCache::getValueAt: read failed"); | 257 ::perror("MatrixFileCache::getValueAt: read failed"); |
195 return 0.f; | 258 value = 0.f; |
196 } | 259 } |
260 if (r > 0) m_off += r; | |
197 return value; | 261 return value; |
198 } | 262 } |
199 | 263 |
200 void | 264 void |
201 MatrixFileCache::getColumnAt(size_t x, float *values) const | 265 MatrixFileCache::getColumnAt(size_t x, float *values) const |
202 { | 266 { |
203 if (m_rw > 0 && x >= m_rx && x < m_rx + m_rw) { | 267 if (m_rw > 0 && x >= m_rx && x < m_rx + m_rw) { |
204 for (size_t y = 0; y < m_height; ++y) { | 268 float *rp = getRegionPtr(x, 0); |
205 values[y] = m_range[x - m_rx][y]; | 269 if (rp) { |
270 for (size_t y = 0; y < m_height; ++y) { | |
271 values[y] = rp[y]; | |
272 } | |
273 return; | |
274 } | |
275 } else if (!m_userRegion) { | |
276 if (autoSetRegion(x)) { | |
277 float *rp = getRegionPtr(x, 0); | |
278 if (rp) { | |
279 for (size_t y = 0; y < m_height; ++y) { | |
280 values[y] = rp[y]; | |
281 } | |
282 return; | |
283 } | |
206 } | 284 } |
207 } | 285 } |
208 | 286 |
209 if (!seekTo(x, 0)) return; | 287 if (!seekTo(x, 0)) return; |
210 if (::read(m_fd, values, m_height * sizeof(float)) != m_height * sizeof(float)) { | 288 ssize_t r = ::read(m_fd, values, m_height * sizeof(float)); |
289 if (r != m_height * sizeof(float)) { | |
211 ::perror("MatrixFileCache::getColumnAt: read failed"); | 290 ::perror("MatrixFileCache::getColumnAt: read failed"); |
212 } | 291 } |
213 return; | 292 if (r > 0) m_off += r; |
214 } | 293 } |
215 | 294 |
216 void | 295 void |
217 MatrixFileCache::setValueAt(size_t x, size_t y, float value) | 296 MatrixFileCache::setValueAt(size_t x, size_t y, float value) |
218 { | 297 { |
221 << std::endl; | 300 << std::endl; |
222 return; | 301 return; |
223 } | 302 } |
224 | 303 |
225 if (!seekTo(x, y)) return; | 304 if (!seekTo(x, y)) return; |
226 if (::write(m_fd, &value, sizeof(float)) != sizeof(float)) { | 305 ssize_t w = ::write(m_fd, &value, sizeof(float)); |
306 if (w != sizeof(float)) { | |
227 ::perror("WARNING: MatrixFileCache::setValueAt: write failed"); | 307 ::perror("WARNING: MatrixFileCache::setValueAt: write failed"); |
228 } | 308 } |
229 | 309 if (w > 0) m_off += w; |
230 //... update range as appropriate | 310 |
311 //... update region as appropriate | |
231 } | 312 } |
232 | 313 |
233 void | 314 void |
234 MatrixFileCache::setColumnAt(size_t x, float *values) | 315 MatrixFileCache::setColumnAt(size_t x, float *values) |
235 { | 316 { |
238 << std::endl; | 319 << std::endl; |
239 return; | 320 return; |
240 } | 321 } |
241 | 322 |
242 if (!seekTo(x, 0)) return; | 323 if (!seekTo(x, 0)) return; |
243 if (::write(m_fd, values, m_height * sizeof(float)) != m_height * sizeof(float)) { | 324 ssize_t w = ::write(m_fd, values, m_height * sizeof(float)); |
325 if (w != m_height * sizeof(float)) { | |
244 ::perror("WARNING: MatrixFileCache::setColumnAt: write failed"); | 326 ::perror("WARNING: MatrixFileCache::setColumnAt: write failed"); |
245 } | 327 } |
246 | 328 if (w > 0) m_off += w; |
247 //... update range as appropriate | 329 |
330 //... update region as appropriate | |
331 } | |
332 | |
333 float * | |
334 MatrixFileCache::getRegionPtr(size_t x, size_t y) const | |
335 { | |
336 if (m_rw == 0) return 0; | |
337 | |
338 float *region = m_region; | |
339 | |
340 if (m_mmapOff > 0) { | |
341 char *cr = (char *)m_region; | |
342 cr += m_mmapOff; | |
343 region = (float *)cr; | |
344 } | |
345 | |
346 float *ptr = &(region[(x - m_rx) * m_height + y]); | |
347 | |
348 // std::cerr << "getRegionPtr(" << x << "," << y << "): region is " << m_region << ", returning " << ptr << std::endl; | |
349 return ptr; | |
350 } | |
351 | |
352 bool | |
353 MatrixFileCache::autoSetRegion(size_t x) const | |
354 { | |
355 size_t rx = x; | |
356 size_t rw = m_autoRegionWidth; | |
357 size_t left = rw / 4; | |
358 if (x < m_rx) left = (rw * 3) / 4; | |
359 if (rx > left) rx -= left; | |
360 else rx = 0; | |
361 if (rx + rw > m_width) rw = m_width - rx; | |
362 return setRegion(rx, rw, false); | |
363 } | |
364 | |
365 bool | |
366 MatrixFileCache::setRegion(size_t x, size_t width, bool user) const | |
367 { | |
368 if (!user && m_userRegion) return false; | |
369 if (m_rw > 0 && x >= m_rx && x + width <= m_rx + m_rw) return true; | |
370 | |
371 if (m_rw > 0) { | |
372 if (m_mmapped) { | |
373 #ifdef HAVE_MMAP | |
374 ::munmap(m_region, m_mmapSize); | |
375 std::cerr << "unmapped " << m_mmapSize << " at " << m_region << std::endl; | |
376 #endif | |
377 } else { | |
378 delete[] m_region; | |
379 } | |
380 m_region = 0; | |
381 m_mmapped = false; | |
382 m_mmapSize = 0; | |
383 m_mmapOff = 0; | |
384 m_rw = 0; | |
385 } | |
386 | |
387 if (width == 0) { | |
388 return true; | |
389 } | |
390 | |
391 #ifdef HAVE_MMAP | |
392 | |
393 if (m_preferMmap) { | |
394 | |
395 size_t mmapSize = m_height * width * sizeof(float); | |
396 off_t offset = m_headerSize + (x * m_height) * sizeof(float); | |
397 int pagesize = getpagesize(); | |
398 off_t aligned = (offset / pagesize) * pagesize; | |
399 size_t mmapOff = offset - aligned; | |
400 mmapSize += mmapOff; | |
401 | |
402 m_region = (float *) | |
403 ::mmap(0, mmapSize, PROT_READ, MAP_PRIVATE, m_fd, aligned); | |
404 | |
405 if (m_region == MAP_FAILED) { | |
406 | |
407 ::perror("Mmap failed"); | |
408 std::cerr << "ERROR: MatrixFileCache::setRegion(" << x << ", " | |
409 << width << "): Mmap(0, " << mmapSize | |
410 << ", " << PROT_READ << ", " << MAP_SHARED << ", " << m_fd | |
411 << ", " << aligned << ") failed, falling back to " | |
412 << "non-mmapping code for this cache" << std::endl; | |
413 m_preferMmap = false; | |
414 | |
415 } else { | |
416 | |
417 std::cerr << "mmap succeeded (offset " << aligned << ", size " << mmapSize << ", m_mmapOff " << mmapOff << ") = " << m_region << std::endl; | |
418 | |
419 m_mmapped = true; | |
420 m_mmapSize = mmapSize; | |
421 m_mmapOff = mmapOff; | |
422 m_rx = x; | |
423 m_rw = width; | |
424 if (user) m_userRegion = true; | |
425 // MUNLOCK(m_region, m_mmapSize); | |
426 return true; | |
427 } | |
428 } | |
429 #endif | |
430 | |
431 if (!seekTo(x, 0)) return false; | |
432 | |
433 m_region = new float[width * m_height]; | |
434 | |
435 ssize_t r = ::read(m_fd, m_region, width * m_height * sizeof(float)); | |
436 if (r < 0) { | |
437 ::perror("Read failed"); | |
438 std::cerr << "ERROR: MatrixFileCache::setRegion(" << x << ", " << width | |
439 << ") failed" << std::endl; | |
440 delete[] m_region; | |
441 m_region = 0; | |
442 return false; | |
443 } | |
444 | |
445 m_off += r; | |
446 | |
447 if (r < width * m_height * sizeof(float)) { | |
448 // didn't manage to read the whole thing, but did get something | |
449 std::cerr << "WARNING: MatrixFileCache::setRegion(" << x << ", " << width | |
450 << "): "; | |
451 width = r / (m_height * sizeof(float)); | |
452 std::cerr << "Only got " << width << " columns" << std::endl; | |
453 } | |
454 | |
455 m_rx = x; | |
456 m_rw = width; | |
457 if (m_rw == 0) { | |
458 delete[] m_region; | |
459 m_region = 0; | |
460 } | |
461 | |
462 std::cerr << "MatrixFileCache::setRegion: set region to " << x << ", " << width << std::endl; | |
463 | |
464 if (user) m_userRegion = true; | |
465 if (m_rw > 0) MUNLOCK(m_region, m_rw * m_height); | |
466 return true; | |
248 } | 467 } |
249 | 468 |
250 bool | 469 bool |
251 MatrixFileCache::seekTo(size_t x, size_t y) const | 470 MatrixFileCache::seekTo(size_t x, size_t y) const |
252 { | 471 { |
253 off_t off = m_headerSize + (x * m_height + y) * sizeof(float); | 472 off_t off = m_headerSize + (x * m_height + y) * sizeof(float); |
254 if (off == m_off) return true; | 473 if (off == m_off) return true; |
474 | |
475 if (m_mode == ReadWrite) { | |
476 std::cerr << "writer: "; | |
477 std::cerr << "seek required (from " << m_off << " to " << off << ")" << std::endl; | |
478 } | |
255 | 479 |
256 if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) { | 480 if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) { |
257 ::perror("Seek failed"); | 481 ::perror("Seek failed"); |
258 std::cerr << "ERROR: MatrixFileCache::seekTo(" << x << ", " << y | 482 std::cerr << "ERROR: MatrixFileCache::seekTo(" << x << ", " << y |
259 << ") failed" << std::endl; | 483 << ") failed" << std::endl; |