svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006-2009 Chris Cannam and QMUL. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "MatrixFile.h" 00017 #include "base/TempDirectory.h" 00018 #include "system/System.h" 00019 #include "base/Profiler.h" 00020 #include "base/Exceptions.h" 00021 #include "base/Thread.h" 00022 00023 #include <sys/types.h> 00024 #include <sys/stat.h> 00025 #include <fcntl.h> 00026 #include <unistd.h> 00027 00028 #include <iostream> 00029 00030 #include <cstdio> 00031 #include <cassert> 00032 00033 #include <cstdlib> 00034 00035 #include <QFileInfo> 00036 #include <QDir> 00037 00038 //#define DEBUG_MATRIX_FILE 1 00039 //#define DEBUG_MATRIX_FILE_READ_SET 1 00040 00041 #ifdef DEBUG_MATRIX_FILE_READ_SET 00042 #ifndef DEBUG_MATRIX_FILE 00043 #define DEBUG_MATRIX_FILE 1 00044 #endif 00045 #endif 00046 00047 std::map<QString, int> MatrixFile::m_refcount; 00048 QMutex MatrixFile::m_createMutex; 00049 00050 static size_t totalStorage = 0; 00051 static size_t totalCount = 0; 00052 static size_t openCount = 0; 00053 00054 MatrixFile::MatrixFile(QString fileBase, Mode mode, 00055 int cellSize, int width, int height) : 00056 m_fd(-1), 00057 m_mode(mode), 00058 m_flags(0), 00059 m_fmode(0), 00060 m_cellSize(cellSize), 00061 m_width(width), 00062 m_height(height), 00063 m_headerSize(2 * sizeof(int)), 00064 m_setColumns(0), 00065 m_autoClose(false), 00066 m_readyToReadColumn(-1) 00067 { 00068 Profiler profiler("MatrixFile::MatrixFile", true); 00069 00070 #ifdef DEBUG_MATRIX_FILE 00071 SVDEBUG << "MatrixFile::MatrixFile(" << fileBase << ", " << int(mode) << ", " << cellSize << ", " << width << ", " << height << ")" << endl; 00072 #endif 00073 00074 m_createMutex.lock(); 00075 00076 QDir tempDir(TempDirectory::getInstance()->getPath()); 00077 QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase))); 00078 bool newFile = !QFileInfo(fileName).exists(); 00079 00080 if (newFile && m_mode == ReadOnly) { 00081 cerr << "ERROR: MatrixFile::MatrixFile: Read-only mode " 00082 << "specified, but cache file does not exist" << endl; 00083 throw FileNotFound(fileName); 00084 } 00085 00086 if (!newFile && m_mode == WriteOnly) { 00087 cerr << "ERROR: MatrixFile::MatrixFile: Write-only mode " 00088 << "specified, but file already exists" << endl; 00089 throw FileOperationFailed(fileName, "create"); 00090 } 00091 00092 m_flags = 0; 00093 m_fmode = S_IRUSR | S_IWUSR; 00094 00095 if (m_mode == WriteOnly) { 00096 m_flags = O_WRONLY | O_CREAT; 00097 } else { 00098 m_flags = O_RDONLY; 00099 } 00100 00101 #ifdef _WIN32 00102 m_flags |= O_BINARY; 00103 #endif 00104 00105 #ifdef DEBUG_MATRIX_FILE 00106 cerr << "MatrixFile(" << this << ")::MatrixFile: opening " << fileName << "..." << endl; 00107 #endif 00108 00109 if ((m_fd = ::open(fileName.toLocal8Bit(), m_flags, m_fmode)) < 0) { 00110 ::perror("Open failed"); 00111 cerr << "ERROR: MatrixFile::MatrixFile: " 00112 << "Failed to open cache file \"" 00113 << fileName << "\""; 00114 if (m_mode == WriteOnly) cerr << " for writing"; 00115 cerr << endl; 00116 throw FailedToOpenFile(fileName); 00117 } 00118 00119 m_createMutex.unlock(); 00120 00121 #ifdef DEBUG_MATRIX_FILE 00122 cerr << "MatrixFile(" << this << ")::MatrixFile: fd is " << m_fd << endl; 00123 #endif 00124 00125 if (newFile) { 00126 initialise(); // write header and "unwritten" column tags 00127 } else { 00128 int header[2]; 00129 if (::read(m_fd, header, 2 * sizeof(int)) < 0) { 00130 ::perror("MatrixFile::MatrixFile: read failed"); 00131 cerr << "ERROR: MatrixFile::MatrixFile: " 00132 << "Failed to read header (fd " << m_fd << ", file \"" 00133 << fileName << "\")" << endl; 00134 throw FileReadFailed(fileName); 00135 } 00136 if (header[0] != m_width || header[1] != m_height) { 00137 cerr << "ERROR: MatrixFile::MatrixFile: " 00138 << "Dimensions in file header (" << header[0] << "x" 00139 << header[1] << ") differ from expected dimensions " 00140 << m_width << "x" << m_height << endl; 00141 throw FailedToOpenFile(fileName); 00142 } 00143 } 00144 00145 m_fileName = fileName; 00146 ++m_refcount[fileName]; 00147 00148 #ifdef DEBUG_MATRIX_FILE 00149 cerr << "MatrixFile[" << m_fd << "]::MatrixFile: File " << fileName << ", ref " << m_refcount[fileName] << endl; 00150 00151 cerr << "MatrixFile[" << m_fd << "]::MatrixFile: Done, size is " << "(" << m_width << ", " << m_height << ")" << endl; 00152 #endif 00153 00154 ++totalCount; 00155 ++openCount; 00156 } 00157 00158 MatrixFile::~MatrixFile() 00159 { 00160 if (m_fd >= 0) { 00161 if (::close(m_fd) < 0) { 00162 ::perror("MatrixFile::~MatrixFile: close failed"); 00163 } 00164 openCount --; 00165 } 00166 00167 QMutexLocker locker(&m_createMutex); 00168 00169 delete m_setColumns; 00170 00171 if (m_fileName != "") { 00172 00173 if (--m_refcount[m_fileName] == 0) { 00174 00175 if (::unlink(m_fileName.toLocal8Bit())) { 00176 cerr << "WARNING: MatrixFile::~MatrixFile: reference count reached 0, but failed to unlink file \"" << m_fileName << "\"" << endl; 00177 } else { 00178 cerr << "deleted " << m_fileName << endl; 00179 } 00180 } 00181 } 00182 00183 if (m_mode == WriteOnly) { 00184 totalStorage -= (m_headerSize + (m_width * m_height * m_cellSize) + m_width); 00185 } 00186 totalCount --; 00187 00188 #ifdef DEBUG_MATRIX_FILE 00189 cerr << "MatrixFile[" << m_fd << "]::~MatrixFile: " << endl; 00190 cerr << "MatrixFile: Total storage now " << totalStorage/1024 << "K in " << totalCount << " instances (" << openCount << " open)" << endl; 00191 #endif 00192 } 00193 00194 void 00195 MatrixFile::initialise() 00196 { 00197 Profiler profiler("MatrixFile::initialise", true); 00198 00199 assert(m_mode == WriteOnly); 00200 00201 m_setColumns = new ResizeableBitset(m_width); 00202 00203 off_t off = m_headerSize + (m_width * m_height * m_cellSize) + m_width; 00204 00205 #ifdef DEBUG_MATRIX_FILE 00206 cerr << "MatrixFile[" << m_fd << "]::initialise(" << m_width << ", " << m_height << "): cell size " << m_cellSize << ", header size " << m_headerSize << ", resizing file" << endl; 00207 #endif 00208 00209 if (::lseek(m_fd, off - 1, SEEK_SET) < 0) { 00210 ::perror("ERROR: MatrixFile::initialise: seek to end failed"); 00211 throw FileOperationFailed(m_fileName, "lseek"); 00212 } 00213 00214 unsigned char byte = 0; 00215 if (::write(m_fd, &byte, 1) != 1) { 00216 ::perror("ERROR: MatrixFile::initialise: write at end failed"); 00217 throw FileOperationFailed(m_fileName, "write"); 00218 } 00219 00220 if (::lseek(m_fd, 0, SEEK_SET) < 0) { 00221 ::perror("ERROR: MatrixFile::initialise: Seek to write header failed"); 00222 throw FileOperationFailed(m_fileName, "lseek"); 00223 } 00224 00225 int header[2]; 00226 header[0] = m_width; 00227 header[1] = m_height; 00228 if (::write(m_fd, header, 2 * sizeof(int)) != 2 * sizeof(int)) { 00229 ::perror("ERROR: MatrixFile::initialise: Failed to write header"); 00230 throw FileOperationFailed(m_fileName, "write"); 00231 } 00232 00233 if (m_mode == WriteOnly) { 00234 totalStorage += (m_headerSize + (m_width * m_height * m_cellSize) + m_width); 00235 } 00236 00237 #ifdef DEBUG_MATRIX_FILE 00238 cerr << "MatrixFile[" << m_fd << "]::initialise(" << m_width << ", " << m_height << "): storage " 00239 << (m_headerSize + m_width * m_height * m_cellSize + m_width) << endl; 00240 00241 cerr << "MatrixFile: Total storage " << totalStorage/1024 << "K" << endl; 00242 #endif 00243 00244 seekTo(0); 00245 } 00246 00247 void 00248 MatrixFile::close() 00249 { 00250 #ifdef DEBUG_MATRIX_FILE 00251 SVDEBUG << "MatrixFile::close()" << endl; 00252 #endif 00253 if (m_fd >= 0) { 00254 if (::close(m_fd) < 0) { 00255 ::perror("MatrixFile::close: close failed"); 00256 } 00257 m_fd = -1; 00258 -- openCount; 00259 #ifdef DEBUG_MATRIX_FILE 00260 cerr << "MatrixFile: Now " << openCount << " open instances" << endl; 00261 #endif 00262 } 00263 } 00264 00265 void 00266 MatrixFile::getColumnAt(int x, void *data) 00267 { 00268 assert(m_mode == ReadOnly); 00269 00270 #ifdef DEBUG_MATRIX_FILE_READ_SET 00271 cerr << "MatrixFile[" << m_fd << "]::getColumnAt(" << x << ")" << endl; 00272 #endif 00273 00274 Profiler profiler("MatrixFile::getColumnAt"); 00275 00276 ssize_t r = -1; 00277 00278 if (m_readyToReadColumn < 0 || 00279 m_readyToReadColumn != x) { 00280 00281 unsigned char set = 0; 00282 if (!seekTo(x)) { 00283 cerr << "ERROR: MatrixFile::getColumnAt(" << x << "): Seek failed" << endl; 00284 throw FileOperationFailed(m_fileName, "seek"); 00285 } 00286 00287 r = ::read(m_fd, &set, 1); 00288 if (r < 0) { 00289 ::perror("MatrixFile::getColumnAt: read failed"); 00290 throw FileReadFailed(m_fileName); 00291 } 00292 if (!set) { 00293 cerr << "MatrixFile[" << m_fd << "]::getColumnAt(" << x << "): Column has not been set" << endl; 00294 return; 00295 } 00296 } 00297 00298 r = ::read(m_fd, data, m_height * m_cellSize); 00299 if (r < 0) { 00300 ::perror("MatrixFile::getColumnAt: read failed"); 00301 throw FileReadFailed(m_fileName); 00302 } 00303 } 00304 00305 bool 00306 MatrixFile::haveSetColumnAt(int x) const 00307 { 00308 if (m_mode == WriteOnly) { 00309 return m_setColumns->get(x); 00310 } 00311 00312 if (m_readyToReadColumn >= 0 && 00313 int(m_readyToReadColumn) == x) return true; 00314 00315 Profiler profiler("MatrixFile::haveSetColumnAt"); 00316 00317 #ifdef DEBUG_MATRIX_FILE_READ_SET 00318 cerr << "MatrixFile[" << m_fd << "]::haveSetColumnAt(" << x << ")" << endl; 00319 // cerr << "."; 00320 #endif 00321 00322 unsigned char set = 0; 00323 if (!seekTo(x)) { 00324 cerr << "ERROR: MatrixFile::haveSetColumnAt(" << x << "): Seek failed" << endl; 00325 throw FileOperationFailed(m_fileName, "seek"); 00326 } 00327 00328 ssize_t r = -1; 00329 r = ::read(m_fd, &set, 1); 00330 if (r < 0) { 00331 ::perror("MatrixFile::haveSetColumnAt: read failed"); 00332 throw FileReadFailed(m_fileName); 00333 } 00334 00335 if (set) m_readyToReadColumn = int(x); 00336 00337 return set; 00338 } 00339 00340 void 00341 MatrixFile::setColumnAt(int x, const void *data) 00342 { 00343 assert(m_mode == WriteOnly); 00344 if (m_fd < 0) return; // closed 00345 00346 #ifdef DEBUG_MATRIX_FILE_READ_SET 00347 cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << ")" << endl; 00348 // cerr << "."; 00349 #endif 00350 00351 ssize_t w = 0; 00352 00353 if (!seekTo(x)) { 00354 cerr << "ERROR: MatrixFile::setColumnAt(" << x << "): Seek failed" << endl; 00355 throw FileOperationFailed(m_fileName, "seek"); 00356 } 00357 00358 unsigned char set = 0; 00359 w = ::write(m_fd, &set, 1); 00360 if (w != 1) { 00361 ::perror("WARNING: MatrixFile::setColumnAt: write failed (1)"); 00362 throw FileOperationFailed(m_fileName, "write"); 00363 } 00364 00365 w = ::write(m_fd, data, m_height * m_cellSize); 00366 if (w != ssize_t(m_height * m_cellSize)) { 00367 ::perror("WARNING: MatrixFile::setColumnAt: write failed (2)"); 00368 throw FileOperationFailed(m_fileName, "write"); 00369 } 00370 /* 00371 if (x == 0) { 00372 cerr << "Wrote " << m_height * m_cellSize << " bytes, as follows:" << endl; 00373 for (int i = 0; i < m_height * m_cellSize; ++i) { 00374 cerr << (int)(((char *)data)[i]) << " "; 00375 } 00376 cerr << endl; 00377 } 00378 */ 00379 if (!seekTo(x)) { 00380 cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << "): Seek failed" << endl; 00381 throw FileOperationFailed(m_fileName, "seek"); 00382 } 00383 00384 set = 1; 00385 w = ::write(m_fd, &set, 1); 00386 if (w != 1) { 00387 ::perror("WARNING: MatrixFile::setColumnAt: write failed (3)"); 00388 throw FileOperationFailed(m_fileName, "write"); 00389 } 00390 00391 m_setColumns->set(x); 00392 if (m_autoClose) { 00393 if (m_setColumns->isAllOn()) { 00394 #ifdef DEBUG_MATRIX_FILE 00395 cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << "): All columns set: auto-closing" << endl; 00396 #endif 00397 close(); 00398 /* 00399 } else { 00400 int set = 0; 00401 for (int i = 0; i < m_width; ++i) { 00402 if (m_setColumns->get(i)) ++set; 00403 } 00404 cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << "): Auto-close on, but not all columns set yet (" << set << " of " << m_width << ")" << endl; 00405 */ 00406 } 00407 } 00408 } 00409 00410 bool 00411 MatrixFile::seekTo(int x) const 00412 { 00413 if (m_fd < 0) { 00414 cerr << "ERROR: MatrixFile::seekTo: File not open" << endl; 00415 return false; 00416 } 00417 00418 m_readyToReadColumn = -1; // not ready, unless this is subsequently re-set 00419 00420 off_t off = m_headerSize + x * m_height * m_cellSize + x; 00421 00422 #ifdef DEBUG_MATRIX_FILE_READ_SET 00423 if (m_mode == ReadOnly) { 00424 cerr << "MatrixFile[" << m_fd << "]::seekTo(" << x << "): off = " << off << endl; 00425 } 00426 #endif 00427 00428 #ifdef DEBUG_MATRIX_FILE_READ_SET 00429 cerr << "MatrixFile[" << m_fd << "]::seekTo(" << x << "): off = " << off << endl; 00430 #endif 00431 00432 if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) { 00433 ::perror("Seek failed"); 00434 cerr << "ERROR: MatrixFile::seekTo(" << x 00435 << ") = " << off << " failed" << endl; 00436 return false; 00437 } 00438 00439 return true; 00440 } 00441