annotate base/MatrixFileCache.cpp @ 93:27d726916ab3

* Remove mmap-based code again. We can be more efficient with read().
author Chris Cannam
date Wed, 03 May 2006 16:47:04 +0000
parents 1dcf41ed3863
children 040a151d0897
rev   line source
Chris@87 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@87 2
Chris@87 3 /*
Chris@87 4 Sonic Visualiser
Chris@87 5 An audio file viewer and annotation editor.
Chris@87 6 Centre for Digital Music, Queen Mary, University of London.
Chris@87 7 This file copyright 2006 Chris Cannam.
Chris@87 8
Chris@87 9 This program is free software; you can redistribute it and/or
Chris@87 10 modify it under the terms of the GNU General Public License as
Chris@87 11 published by the Free Software Foundation; either version 2 of the
Chris@87 12 License, or (at your option) any later version. See the file
Chris@87 13 COPYING included with this distribution for more information.
Chris@87 14 */
Chris@87 15
Chris@87 16 #include "MatrixFileCache.h"
Chris@87 17 #include "base/TempDirectory.h"
Chris@90 18 #include "base/System.h"
Chris@87 19
Chris@87 20 #include <sys/types.h>
Chris@87 21 #include <sys/stat.h>
Chris@87 22 #include <fcntl.h>
Chris@87 23 #include <unistd.h>
Chris@87 24
Chris@87 25 #include <iostream>
Chris@87 26
Chris@87 27 #include <cstdio>
Chris@87 28
Chris@87 29 #include <QFileInfo>
Chris@91 30 #include <QFile>
Chris@87 31 #include <QDir>
Chris@87 32
Chris@91 33 std::map<QString, int> MatrixFileCache::m_refcount;
Chris@91 34 QMutex MatrixFileCache::m_refcountMutex;
Chris@91 35
Chris@90 36 //!!! This class is a work in progress -- it does only as much as we
Chris@90 37 // need for the current SpectrogramLayer. Slated for substantial
Chris@90 38 // refactoring and extension.
Chris@90 39
Chris@87 40 MatrixFileCache::MatrixFileCache(QString fileBase, Mode mode) :
Chris@87 41 m_fd(-1),
Chris@87 42 m_mode(mode),
Chris@87 43 m_width(0),
Chris@87 44 m_height(0),
Chris@90 45 m_headerSize(2 * sizeof(size_t)),
Chris@91 46 m_autoRegionWidth(256),
Chris@90 47 m_off(-1),
Chris@87 48 m_rx(0),
Chris@87 49 m_rw(0),
Chris@90 50 m_userRegion(false),
Chris@93 51 m_region(0)
Chris@87 52 {
Chris@90 53 // Ensure header size is a multiple of the size of our data (for
Chris@90 54 // alignment purposes)
Chris@90 55 size_t hs = ((m_headerSize / sizeof(float)) * sizeof(float));
Chris@90 56 if (hs != m_headerSize) m_headerSize = hs + sizeof(float);
Chris@90 57
Chris@87 58 QDir tempDir(TempDirectory::instance()->getPath());
Chris@87 59 QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase)));
Chris@87 60 bool newFile = !QFileInfo(fileName).exists();
Chris@87 61
Chris@87 62 if (newFile && mode == ReadOnly) {
Chris@87 63 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: Read-only mode "
Chris@87 64 << "specified, but cache file does not exist" << std::endl;
Chris@87 65 return;
Chris@87 66 }
Chris@87 67
Chris@87 68 int flags = 0;
Chris@87 69 mode_t fmode = S_IRUSR | S_IWUSR;
Chris@87 70
Chris@87 71 if (mode == ReadWrite) {
Chris@87 72 flags = O_RDWR | O_CREAT;
Chris@87 73 } else {
Chris@87 74 flags = O_RDONLY;
Chris@87 75 }
Chris@87 76
Chris@90 77 if ((m_fd = ::open(fileName.toLocal8Bit(), flags, fmode)) < 0) {
Chris@87 78 ::perror("Open failed");
Chris@87 79 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: "
Chris@87 80 << "Failed to open cache file \""
Chris@87 81 << fileName.toStdString() << "\"";
Chris@87 82 if (mode == ReadWrite) std::cerr << " for writing";
Chris@87 83 std::cerr << std::endl;
Chris@91 84 return;
Chris@87 85 }
Chris@87 86
Chris@87 87 if (newFile) {
Chris@87 88 resize(0, 0); // write header
Chris@87 89 } else {
Chris@87 90 size_t header[2];
Chris@90 91 if (::read(m_fd, header, 2 * sizeof(size_t)) < 0) {
Chris@87 92 perror("Read failed");
Chris@87 93 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: "
Chris@90 94 << "Failed to read header (fd " << m_fd << ", file \""
Chris@90 95 << fileName.toStdString() << "\")" << std::endl;
Chris@87 96 return;
Chris@87 97 }
Chris@87 98 m_width = header[0];
Chris@87 99 m_height = header[1];
Chris@87 100 seekTo(0, 0);
Chris@87 101 }
Chris@87 102
Chris@91 103 m_fileName = fileName;
Chris@91 104 QMutexLocker locker(&m_refcountMutex);
Chris@91 105 ++m_refcount[fileName];
Chris@91 106
Chris@90 107 std::cerr << "MatrixFileCache::MatrixFileCache: Done, size is " << "(" << m_width << ", " << m_height << ")" << std::endl;
Chris@87 108
Chris@87 109 }
Chris@87 110
Chris@87 111 MatrixFileCache::~MatrixFileCache()
Chris@87 112 {
Chris@90 113 if (m_rw > 0) {
Chris@93 114 delete[] m_region;
Chris@90 115 }
Chris@90 116
Chris@87 117 if (m_fd >= 0) {
Chris@87 118 if (::close(m_fd) < 0) {
Chris@87 119 ::perror("MatrixFileCache::~MatrixFileCache: close failed");
Chris@87 120 }
Chris@87 121 }
Chris@90 122
Chris@91 123 if (m_fileName != "") {
Chris@91 124 QMutexLocker locker(&m_refcountMutex);
Chris@91 125 if (--m_refcount[m_fileName] == 0) {
Chris@91 126 if (!QFile(m_fileName).remove()) {
Chris@91 127 std::cerr << "WARNING: MatrixFileCache::~MatrixFileCache: reference count reached 0, but failed to unlink file \"" << m_fileName.toStdString() << "\"" << std::endl;
Chris@91 128 } else {
Chris@91 129 std::cerr << "deleted " << m_fileName.toStdString() << std::endl;
Chris@91 130 }
Chris@91 131 }
Chris@91 132 }
Chris@87 133 }
Chris@87 134
Chris@87 135 size_t
Chris@87 136 MatrixFileCache::getWidth() const
Chris@87 137 {
Chris@87 138 return m_width;
Chris@87 139 }
Chris@87 140
Chris@87 141 size_t
Chris@87 142 MatrixFileCache::getHeight() const
Chris@87 143 {
Chris@87 144 return m_height;
Chris@87 145 }
Chris@87 146
Chris@87 147 void
Chris@87 148 MatrixFileCache::resize(size_t w, size_t h)
Chris@87 149 {
Chris@87 150 if (m_mode != ReadWrite) {
Chris@87 151 std::cerr << "ERROR: MatrixFileCache::resize called on read-only cache"
Chris@87 152 << std::endl;
Chris@87 153 return;
Chris@87 154 }
Chris@87 155
Chris@87 156 off_t off = m_headerSize + (w * h * sizeof(float));
Chris@87 157
Chris@87 158 if (w * h > m_width * m_height) {
Chris@87 159
Chris@93 160 /*!!!
Chris@90 161 // If we're going to mmap the file, we need to ensure it's long
Chris@90 162 // enough beforehand
Chris@90 163
Chris@90 164 if (m_preferMmap) {
Chris@90 165
Chris@90 166 if (::lseek(m_fd, off - sizeof(float), SEEK_SET) == (off_t)-1) {
Chris@90 167 ::perror("Seek failed");
Chris@90 168 std::cerr << "ERROR: MatrixFileCache::resize(" << w << ", "
Chris@90 169 << h << "): seek failed, cannot resize" << std::endl;
Chris@90 170 return;
Chris@90 171 }
Chris@90 172
Chris@90 173 // guess this requires efficient support for sparse files
Chris@90 174
Chris@90 175 float f(0);
Chris@90 176 if (::write(m_fd, &f, sizeof(float)) != sizeof(float)) {
Chris@90 177 ::perror("WARNING: MatrixFileCache::resize: write failed");
Chris@90 178 }
Chris@87 179 }
Chris@93 180 */
Chris@87 181 } else {
Chris@87 182
Chris@87 183 if (::ftruncate(m_fd, off) < 0) {
Chris@90 184 ::perror("WARNING: MatrixFileCache::resize: ftruncate failed");
Chris@87 185 }
Chris@87 186 }
Chris@87 187
Chris@87 188 m_width = 0;
Chris@87 189 m_height = 0;
Chris@87 190 m_off = 0;
Chris@87 191
Chris@87 192 if (::lseek(m_fd, 0, SEEK_SET) == (off_t)-1) {
Chris@87 193 ::perror("ERROR: MatrixFileCache::resize: Seek to write header failed");
Chris@87 194 return;
Chris@87 195 }
Chris@87 196
Chris@87 197 size_t header[2];
Chris@87 198 header[0] = w;
Chris@87 199 header[1] = h;
Chris@87 200 if (::write(m_fd, header, 2 * sizeof(size_t)) != 2 * sizeof(size_t)) {
Chris@87 201 ::perror("ERROR: MatrixFileCache::resize: Failed to write header");
Chris@87 202 return;
Chris@87 203 }
Chris@87 204
Chris@87 205 m_width = w;
Chris@87 206 m_height = h;
Chris@87 207
Chris@87 208 seekTo(0, 0);
Chris@87 209 }
Chris@87 210
Chris@87 211 void
Chris@87 212 MatrixFileCache::reset()
Chris@87 213 {
Chris@87 214 if (m_mode != ReadWrite) {
Chris@87 215 std::cerr << "ERROR: MatrixFileCache::reset called on read-only cache"
Chris@87 216 << std::endl;
Chris@87 217 return;
Chris@87 218 }
Chris@87 219
Chris@90 220 float *emptyCol = new float[m_height];
Chris@90 221 for (size_t y = 0; y < m_height; ++y) emptyCol[y] = 0.f;
Chris@90 222
Chris@90 223 seekTo(0, 0);
Chris@90 224 for (size_t x = 0; x < m_width; ++x) setColumnAt(x, emptyCol);
Chris@90 225
Chris@90 226 delete[] emptyCol;
Chris@87 227 }
Chris@87 228
Chris@87 229 void
Chris@90 230 MatrixFileCache::setRegionOfInterest(size_t x, size_t width)
Chris@87 231 {
Chris@90 232 setRegion(x, width, true);
Chris@90 233 }
Chris@90 234
Chris@90 235 void
Chris@90 236 MatrixFileCache::clearRegionOfInterest()
Chris@90 237 {
Chris@90 238 m_userRegion = false;
Chris@87 239 }
Chris@87 240
Chris@87 241 float
Chris@87 242 MatrixFileCache::getValueAt(size_t x, size_t y) const
Chris@87 243 {
Chris@87 244 if (m_rw > 0 && x >= m_rx && x < m_rx + m_rw) {
Chris@90 245 float *rp = getRegionPtr(x, y);
Chris@90 246 if (rp) return *rp;
Chris@90 247 } else if (!m_userRegion) {
Chris@90 248 if (autoSetRegion(x)) {
Chris@90 249 float *rp = getRegionPtr(x, y);
Chris@90 250 if (rp) return *rp;
Chris@91 251 else return 0.f;
Chris@90 252 }
Chris@87 253 }
Chris@87 254
Chris@87 255 if (!seekTo(x, y)) return 0.f;
Chris@87 256 float value;
Chris@90 257 ssize_t r = ::read(m_fd, &value, sizeof(float));
Chris@91 258 if (r < 0) {
Chris@91 259 ::perror("MatrixFileCache::getValueAt: Read failed");
Chris@91 260 }
Chris@90 261 if (r != sizeof(float)) {
Chris@90 262 value = 0.f;
Chris@87 263 }
Chris@90 264 if (r > 0) m_off += r;
Chris@87 265 return value;
Chris@87 266 }
Chris@87 267
Chris@87 268 void
Chris@87 269 MatrixFileCache::getColumnAt(size_t x, float *values) const
Chris@87 270 {
Chris@87 271 if (m_rw > 0 && x >= m_rx && x < m_rx + m_rw) {
Chris@90 272 float *rp = getRegionPtr(x, 0);
Chris@90 273 if (rp) {
Chris@90 274 for (size_t y = 0; y < m_height; ++y) {
Chris@90 275 values[y] = rp[y];
Chris@90 276 }
Chris@90 277 }
Chris@91 278 return;
Chris@90 279 } else if (!m_userRegion) {
Chris@90 280 if (autoSetRegion(x)) {
Chris@90 281 float *rp = getRegionPtr(x, 0);
Chris@90 282 if (rp) {
Chris@90 283 for (size_t y = 0; y < m_height; ++y) {
Chris@90 284 values[y] = rp[y];
Chris@90 285 }
Chris@90 286 return;
Chris@90 287 }
Chris@87 288 }
Chris@87 289 }
Chris@87 290
Chris@87 291 if (!seekTo(x, 0)) return;
Chris@90 292 ssize_t r = ::read(m_fd, values, m_height * sizeof(float));
Chris@91 293 if (r < 0) {
Chris@87 294 ::perror("MatrixFileCache::getColumnAt: read failed");
Chris@87 295 }
Chris@90 296 if (r > 0) m_off += r;
Chris@87 297 }
Chris@87 298
Chris@87 299 void
Chris@87 300 MatrixFileCache::setValueAt(size_t x, size_t y, float value)
Chris@87 301 {
Chris@87 302 if (m_mode != ReadWrite) {
Chris@87 303 std::cerr << "ERROR: MatrixFileCache::setValueAt called on read-only cache"
Chris@87 304 << std::endl;
Chris@87 305 return;
Chris@87 306 }
Chris@87 307
Chris@87 308 if (!seekTo(x, y)) return;
Chris@90 309 ssize_t w = ::write(m_fd, &value, sizeof(float));
Chris@90 310 if (w != sizeof(float)) {
Chris@87 311 ::perror("WARNING: MatrixFileCache::setValueAt: write failed");
Chris@87 312 }
Chris@90 313 if (w > 0) m_off += w;
Chris@87 314
Chris@90 315 //... update region as appropriate
Chris@87 316 }
Chris@87 317
Chris@87 318 void
Chris@87 319 MatrixFileCache::setColumnAt(size_t x, float *values)
Chris@87 320 {
Chris@87 321 if (m_mode != ReadWrite) {
Chris@87 322 std::cerr << "ERROR: MatrixFileCache::setColumnAt called on read-only cache"
Chris@87 323 << std::endl;
Chris@87 324 return;
Chris@87 325 }
Chris@87 326
Chris@87 327 if (!seekTo(x, 0)) return;
Chris@90 328 ssize_t w = ::write(m_fd, values, m_height * sizeof(float));
Chris@91 329 if (w != ssize_t(m_height * sizeof(float))) {
Chris@87 330 ::perror("WARNING: MatrixFileCache::setColumnAt: write failed");
Chris@87 331 }
Chris@90 332 if (w > 0) m_off += w;
Chris@87 333
Chris@90 334 //... update region as appropriate
Chris@90 335 }
Chris@90 336
Chris@90 337 float *
Chris@90 338 MatrixFileCache::getRegionPtr(size_t x, size_t y) const
Chris@90 339 {
Chris@90 340 if (m_rw == 0) return 0;
Chris@90 341
Chris@90 342 float *region = m_region;
Chris@90 343
Chris@90 344 float *ptr = &(region[(x - m_rx) * m_height + y]);
Chris@90 345
Chris@90 346 // std::cerr << "getRegionPtr(" << x << "," << y << "): region is " << m_region << ", returning " << ptr << std::endl;
Chris@90 347 return ptr;
Chris@90 348 }
Chris@90 349
Chris@90 350 bool
Chris@90 351 MatrixFileCache::autoSetRegion(size_t x) const
Chris@90 352 {
Chris@90 353 size_t rx = x;
Chris@90 354 size_t rw = m_autoRegionWidth;
Chris@90 355 size_t left = rw / 4;
Chris@90 356 if (x < m_rx) left = (rw * 3) / 4;
Chris@90 357 if (rx > left) rx -= left;
Chris@90 358 else rx = 0;
Chris@90 359 if (rx + rw > m_width) rw = m_width - rx;
Chris@90 360 return setRegion(rx, rw, false);
Chris@90 361 }
Chris@90 362
Chris@90 363 bool
Chris@90 364 MatrixFileCache::setRegion(size_t x, size_t width, bool user) const
Chris@90 365 {
Chris@90 366 if (!user && m_userRegion) return false;
Chris@90 367 if (m_rw > 0 && x >= m_rx && x + width <= m_rx + m_rw) return true;
Chris@90 368
Chris@90 369 if (m_rw > 0) {
Chris@93 370 delete[] m_region;
Chris@90 371 m_region = 0;
Chris@90 372 m_rw = 0;
Chris@90 373 }
Chris@90 374
Chris@90 375 if (width == 0) {
Chris@90 376 return true;
Chris@90 377 }
Chris@90 378
Chris@90 379 if (!seekTo(x, 0)) return false;
Chris@90 380
Chris@90 381 m_region = new float[width * m_height];
Chris@91 382 MUNLOCK(m_region, width * m_height * sizeof(float));
Chris@90 383
Chris@90 384 ssize_t r = ::read(m_fd, m_region, width * m_height * sizeof(float));
Chris@90 385 if (r < 0) {
Chris@90 386 ::perror("Read failed");
Chris@90 387 std::cerr << "ERROR: MatrixFileCache::setRegion(" << x << ", " << width
Chris@90 388 << ") failed" << std::endl;
Chris@90 389 delete[] m_region;
Chris@90 390 m_region = 0;
Chris@90 391 return false;
Chris@90 392 }
Chris@90 393
Chris@90 394 m_off += r;
Chris@90 395
Chris@91 396 if (r < ssize_t(width * m_height * sizeof(float))) {
Chris@90 397 // didn't manage to read the whole thing, but did get something
Chris@90 398 std::cerr << "WARNING: MatrixFileCache::setRegion(" << x << ", " << width
Chris@90 399 << "): ";
Chris@90 400 width = r / (m_height * sizeof(float));
Chris@90 401 std::cerr << "Only got " << width << " columns" << std::endl;
Chris@90 402 }
Chris@90 403
Chris@90 404 m_rx = x;
Chris@90 405 m_rw = width;
Chris@90 406 if (m_rw == 0) {
Chris@90 407 delete[] m_region;
Chris@90 408 m_region = 0;
Chris@90 409 }
Chris@90 410
Chris@90 411 std::cerr << "MatrixFileCache::setRegion: set region to " << x << ", " << width << std::endl;
Chris@90 412
Chris@90 413 if (user) m_userRegion = true;
Chris@90 414 return true;
Chris@87 415 }
Chris@87 416
Chris@87 417 bool
Chris@87 418 MatrixFileCache::seekTo(size_t x, size_t y) const
Chris@87 419 {
Chris@87 420 off_t off = m_headerSize + (x * m_height + y) * sizeof(float);
Chris@87 421 if (off == m_off) return true;
Chris@87 422
Chris@90 423 if (m_mode == ReadWrite) {
Chris@90 424 std::cerr << "writer: ";
Chris@90 425 std::cerr << "seek required (from " << m_off << " to " << off << ")" << std::endl;
Chris@90 426 }
Chris@90 427
Chris@87 428 if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) {
Chris@87 429 ::perror("Seek failed");
Chris@87 430 std::cerr << "ERROR: MatrixFileCache::seekTo(" << x << ", " << y
Chris@87 431 << ") failed" << std::endl;
Chris@87 432 return false;
Chris@87 433 }
Chris@87 434
Chris@87 435 m_off = off;
Chris@87 436 return true;
Chris@87 437 }
Chris@87 438