svcore  1.9
MatrixFile.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006-2009 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "MatrixFile.h"
17 #include "base/TempDirectory.h"
18 #include "system/System.h"
19 #include "base/Profiler.h"
20 #include "base/Exceptions.h"
21 #include "base/Thread.h"
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 
28 #include <iostream>
29 
30 #include <cstdio>
31 #include <cassert>
32 
33 #include <cstdlib>
34 
35 #include <QFileInfo>
36 #include <QDir>
37 
38 //#define DEBUG_MATRIX_FILE 1
39 //#define DEBUG_MATRIX_FILE_READ_SET 1
40 
41 #ifdef DEBUG_MATRIX_FILE_READ_SET
42 #ifndef DEBUG_MATRIX_FILE
43 #define DEBUG_MATRIX_FILE 1
44 #endif
45 #endif
46 
47 std::map<QString, int> MatrixFile::m_refcount;
49 
50 static size_t totalStorage = 0;
51 static size_t totalCount = 0;
52 static size_t openCount = 0;
53 
54 MatrixFile::MatrixFile(QString fileBase, Mode mode,
55  int cellSize, int width, int height) :
56  m_fd(-1),
57  m_mode(mode),
58  m_flags(0),
59  m_fmode(0),
60  m_cellSize(cellSize),
61  m_width(width),
62  m_height(height),
63  m_headerSize(2 * sizeof(int)),
64  m_setColumns(0),
65  m_autoClose(false),
66  m_readyToReadColumn(-1)
67 {
68  Profiler profiler("MatrixFile::MatrixFile", true);
69 
70 #ifdef DEBUG_MATRIX_FILE
71  SVDEBUG << "MatrixFile::MatrixFile(" << fileBase << ", " << int(mode) << ", " << cellSize << ", " << width << ", " << height << ")" << endl;
72 #endif
73 
74  m_createMutex.lock();
75 
76  QDir tempDir(TempDirectory::getInstance()->getPath());
77  QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase)));
78  bool newFile = !QFileInfo(fileName).exists();
79 
80  if (newFile && m_mode == ReadOnly) {
81  cerr << "ERROR: MatrixFile::MatrixFile: Read-only mode "
82  << "specified, but cache file does not exist" << endl;
83  throw FileNotFound(fileName);
84  }
85 
86  if (!newFile && m_mode == WriteOnly) {
87  cerr << "ERROR: MatrixFile::MatrixFile: Write-only mode "
88  << "specified, but file already exists" << endl;
89  throw FileOperationFailed(fileName, "create");
90  }
91 
92  m_flags = 0;
93  m_fmode = S_IRUSR | S_IWUSR;
94 
95  if (m_mode == WriteOnly) {
96  m_flags = O_WRONLY | O_CREAT;
97  } else {
98  m_flags = O_RDONLY;
99  }
100 
101 #ifdef _WIN32
102  m_flags |= O_BINARY;
103 #endif
104 
105 #ifdef DEBUG_MATRIX_FILE
106  cerr << "MatrixFile(" << this << ")::MatrixFile: opening " << fileName << "..." << endl;
107 #endif
108 
109  if ((m_fd = ::open(fileName.toLocal8Bit(), m_flags, m_fmode)) < 0) {
110  ::perror("Open failed");
111  cerr << "ERROR: MatrixFile::MatrixFile: "
112  << "Failed to open cache file \""
113  << fileName << "\"";
114  if (m_mode == WriteOnly) cerr << " for writing";
115  cerr << endl;
116  throw FailedToOpenFile(fileName);
117  }
118 
119  m_createMutex.unlock();
120 
121 #ifdef DEBUG_MATRIX_FILE
122  cerr << "MatrixFile(" << this << ")::MatrixFile: fd is " << m_fd << endl;
123 #endif
124 
125  if (newFile) {
126  initialise(); // write header and "unwritten" column tags
127  } else {
128  int header[2];
129  if (::read(m_fd, header, 2 * sizeof(int)) < 0) {
130  ::perror("MatrixFile::MatrixFile: read failed");
131  cerr << "ERROR: MatrixFile::MatrixFile: "
132  << "Failed to read header (fd " << m_fd << ", file \""
133  << fileName << "\")" << endl;
134  throw FileReadFailed(fileName);
135  }
136  if (header[0] != m_width || header[1] != m_height) {
137  cerr << "ERROR: MatrixFile::MatrixFile: "
138  << "Dimensions in file header (" << header[0] << "x"
139  << header[1] << ") differ from expected dimensions "
140  << m_width << "x" << m_height << endl;
141  throw FailedToOpenFile(fileName);
142  }
143  }
144 
145  m_fileName = fileName;
146  ++m_refcount[fileName];
147 
148 #ifdef DEBUG_MATRIX_FILE
149  cerr << "MatrixFile[" << m_fd << "]::MatrixFile: File " << fileName << ", ref " << m_refcount[fileName] << endl;
150 
151  cerr << "MatrixFile[" << m_fd << "]::MatrixFile: Done, size is " << "(" << m_width << ", " << m_height << ")" << endl;
152 #endif
153 
154  ++totalCount;
155  ++openCount;
156 }
157 
159 {
160  if (m_fd >= 0) {
161  if (::close(m_fd) < 0) {
162  ::perror("MatrixFile::~MatrixFile: close failed");
163  }
164  openCount --;
165  }
166 
167  QMutexLocker locker(&m_createMutex);
168 
169  delete m_setColumns;
170 
171  if (m_fileName != "") {
172 
173  if (--m_refcount[m_fileName] == 0) {
174 
175  if (::unlink(m_fileName.toLocal8Bit())) {
176  cerr << "WARNING: MatrixFile::~MatrixFile: reference count reached 0, but failed to unlink file \"" << m_fileName << "\"" << endl;
177  } else {
178  cerr << "deleted " << m_fileName << endl;
179  }
180  }
181  }
182 
183  if (m_mode == WriteOnly) {
185  }
186  totalCount --;
187 
188 #ifdef DEBUG_MATRIX_FILE
189  cerr << "MatrixFile[" << m_fd << "]::~MatrixFile: " << endl;
190  cerr << "MatrixFile: Total storage now " << totalStorage/1024 << "K in " << totalCount << " instances (" << openCount << " open)" << endl;
191 #endif
192 }
193 
194 void
196 {
197  Profiler profiler("MatrixFile::initialise", true);
198 
199  assert(m_mode == WriteOnly);
200 
202 
203  off_t off = m_headerSize + (m_width * m_height * m_cellSize) + m_width;
204 
205 #ifdef DEBUG_MATRIX_FILE
206  cerr << "MatrixFile[" << m_fd << "]::initialise(" << m_width << ", " << m_height << "): cell size " << m_cellSize << ", header size " << m_headerSize << ", resizing file" << endl;
207 #endif
208 
209  if (::lseek(m_fd, off - 1, SEEK_SET) < 0) {
210  ::perror("ERROR: MatrixFile::initialise: seek to end failed");
211  throw FileOperationFailed(m_fileName, "lseek");
212  }
213 
214  unsigned char byte = 0;
215  if (::write(m_fd, &byte, 1) != 1) {
216  ::perror("ERROR: MatrixFile::initialise: write at end failed");
217  throw FileOperationFailed(m_fileName, "write");
218  }
219 
220  if (::lseek(m_fd, 0, SEEK_SET) < 0) {
221  ::perror("ERROR: MatrixFile::initialise: Seek to write header failed");
222  throw FileOperationFailed(m_fileName, "lseek");
223  }
224 
225  int header[2];
226  header[0] = m_width;
227  header[1] = m_height;
228  if (::write(m_fd, header, 2 * sizeof(int)) != 2 * sizeof(int)) {
229  ::perror("ERROR: MatrixFile::initialise: Failed to write header");
230  throw FileOperationFailed(m_fileName, "write");
231  }
232 
233  if (m_mode == WriteOnly) {
235  }
236 
237 #ifdef DEBUG_MATRIX_FILE
238  cerr << "MatrixFile[" << m_fd << "]::initialise(" << m_width << ", " << m_height << "): storage "
239  << (m_headerSize + m_width * m_height * m_cellSize + m_width) << endl;
240 
241  cerr << "MatrixFile: Total storage " << totalStorage/1024 << "K" << endl;
242 #endif
243 
244  seekTo(0);
245 }
246 
247 void
249 {
250 #ifdef DEBUG_MATRIX_FILE
251  SVDEBUG << "MatrixFile::close()" << endl;
252 #endif
253  if (m_fd >= 0) {
254  if (::close(m_fd) < 0) {
255  ::perror("MatrixFile::close: close failed");
256  }
257  m_fd = -1;
258  -- openCount;
259 #ifdef DEBUG_MATRIX_FILE
260  cerr << "MatrixFile: Now " << openCount << " open instances" << endl;
261 #endif
262  }
263 }
264 
265 void
266 MatrixFile::getColumnAt(int x, void *data)
267 {
268  assert(m_mode == ReadOnly);
269 
270 #ifdef DEBUG_MATRIX_FILE_READ_SET
271  cerr << "MatrixFile[" << m_fd << "]::getColumnAt(" << x << ")" << endl;
272 #endif
273 
274  Profiler profiler("MatrixFile::getColumnAt");
275 
276  ssize_t r = -1;
277 
278  if (m_readyToReadColumn < 0 ||
279  m_readyToReadColumn != x) {
280 
281  unsigned char set = 0;
282  if (!seekTo(x)) {
283  cerr << "ERROR: MatrixFile::getColumnAt(" << x << "): Seek failed" << endl;
284  throw FileOperationFailed(m_fileName, "seek");
285  }
286 
287  r = ::read(m_fd, &set, 1);
288  if (r < 0) {
289  ::perror("MatrixFile::getColumnAt: read failed");
290  throw FileReadFailed(m_fileName);
291  }
292  if (!set) {
293  cerr << "MatrixFile[" << m_fd << "]::getColumnAt(" << x << "): Column has not been set" << endl;
294  return;
295  }
296  }
297 
298  r = ::read(m_fd, data, m_height * m_cellSize);
299  if (r < 0) {
300  ::perror("MatrixFile::getColumnAt: read failed");
301  throw FileReadFailed(m_fileName);
302  }
303 }
304 
305 bool
307 {
308  if (m_mode == WriteOnly) {
309  return m_setColumns->get(x);
310  }
311 
312  if (m_readyToReadColumn >= 0 &&
313  int(m_readyToReadColumn) == x) return true;
314 
315  Profiler profiler("MatrixFile::haveSetColumnAt");
316 
317 #ifdef DEBUG_MATRIX_FILE_READ_SET
318  cerr << "MatrixFile[" << m_fd << "]::haveSetColumnAt(" << x << ")" << endl;
319 // cerr << ".";
320 #endif
321 
322  unsigned char set = 0;
323  if (!seekTo(x)) {
324  cerr << "ERROR: MatrixFile::haveSetColumnAt(" << x << "): Seek failed" << endl;
325  throw FileOperationFailed(m_fileName, "seek");
326  }
327 
328  ssize_t r = -1;
329  r = ::read(m_fd, &set, 1);
330  if (r < 0) {
331  ::perror("MatrixFile::haveSetColumnAt: read failed");
332  throw FileReadFailed(m_fileName);
333  }
334 
335  if (set) m_readyToReadColumn = int(x);
336 
337  return set;
338 }
339 
340 void
341 MatrixFile::setColumnAt(int x, const void *data)
342 {
343  assert(m_mode == WriteOnly);
344  if (m_fd < 0) return; // closed
345 
346 #ifdef DEBUG_MATRIX_FILE_READ_SET
347  cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << ")" << endl;
348 // cerr << ".";
349 #endif
350 
351  ssize_t w = 0;
352 
353  if (!seekTo(x)) {
354  cerr << "ERROR: MatrixFile::setColumnAt(" << x << "): Seek failed" << endl;
355  throw FileOperationFailed(m_fileName, "seek");
356  }
357 
358  unsigned char set = 0;
359  w = ::write(m_fd, &set, 1);
360  if (w != 1) {
361  ::perror("WARNING: MatrixFile::setColumnAt: write failed (1)");
362  throw FileOperationFailed(m_fileName, "write");
363  }
364 
365  w = ::write(m_fd, data, m_height * m_cellSize);
366  if (w != ssize_t(m_height * m_cellSize)) {
367  ::perror("WARNING: MatrixFile::setColumnAt: write failed (2)");
368  throw FileOperationFailed(m_fileName, "write");
369  }
370 /*
371  if (x == 0) {
372  cerr << "Wrote " << m_height * m_cellSize << " bytes, as follows:" << endl;
373  for (int i = 0; i < m_height * m_cellSize; ++i) {
374  cerr << (int)(((char *)data)[i]) << " ";
375  }
376  cerr << endl;
377  }
378 */
379  if (!seekTo(x)) {
380  cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << "): Seek failed" << endl;
381  throw FileOperationFailed(m_fileName, "seek");
382  }
383 
384  set = 1;
385  w = ::write(m_fd, &set, 1);
386  if (w != 1) {
387  ::perror("WARNING: MatrixFile::setColumnAt: write failed (3)");
388  throw FileOperationFailed(m_fileName, "write");
389  }
390 
391  m_setColumns->set(x);
392  if (m_autoClose) {
393  if (m_setColumns->isAllOn()) {
394 #ifdef DEBUG_MATRIX_FILE
395  cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << "): All columns set: auto-closing" << endl;
396 #endif
397  close();
398 /*
399  } else {
400  int set = 0;
401  for (int i = 0; i < m_width; ++i) {
402  if (m_setColumns->get(i)) ++set;
403  }
404  cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << "): Auto-close on, but not all columns set yet (" << set << " of " << m_width << ")" << endl;
405 */
406  }
407  }
408 }
409 
410 bool
411 MatrixFile::seekTo(int x) const
412 {
413  if (m_fd < 0) {
414  cerr << "ERROR: MatrixFile::seekTo: File not open" << endl;
415  return false;
416  }
417 
418  m_readyToReadColumn = -1; // not ready, unless this is subsequently re-set
419 
420  off_t off = m_headerSize + x * m_height * m_cellSize + x;
421 
422 #ifdef DEBUG_MATRIX_FILE_READ_SET
423  if (m_mode == ReadOnly) {
424  cerr << "MatrixFile[" << m_fd << "]::seekTo(" << x << "): off = " << off << endl;
425  }
426 #endif
427 
428 #ifdef DEBUG_MATRIX_FILE_READ_SET
429  cerr << "MatrixFile[" << m_fd << "]::seekTo(" << x << "): off = " << off << endl;
430 #endif
431 
432  if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) {
433  ::perror("Seek failed");
434  cerr << "ERROR: MatrixFile::seekTo(" << x
435  << ") = " << off << " failed" << endl;
436  return false;
437  }
438 
439  return true;
440 }
441 
void initialise()
Definition: MatrixFile.cpp:195
void setColumnAt(int x, const void *data)
Definition: MatrixFile.cpp:341
ResizeableBitset * m_setColumns
Definition: MatrixFile.h:94
static size_t openCount
Definition: MatrixFile.cpp:52
QString m_fileName
Definition: MatrixFile.h:92
bool haveSetColumnAt(int x) const
Definition: MatrixFile.cpp:306
virtual ~MatrixFile()
Definition: MatrixFile.cpp:158
int m_flags
Definition: MatrixFile.h:86
static std::map< QString, int > m_refcount
Definition: MatrixFile.h:101
Mode m_mode
Definition: MatrixFile.h:85
int m_width
Definition: MatrixFile.h:89
void close()
Definition: MatrixFile.cpp:248
int m_cellSize
Definition: MatrixFile.h:88
static TempDirectory * getInstance()
bool seekTo(int col) const
Definition: MatrixFile.cpp:411
bool get(size_t column) const
bool m_autoClose
Definition: MatrixFile.h:95
#define SVDEBUG
Definition: Debug.h:42
MatrixFile(QString fileBase, Mode mode, int cellSize, int width, int height)
Construct a MatrixFile object reading from and/or writing to the matrix file with the given base name...
Definition: MatrixFile.cpp:54
int m_readyToReadColumn
Definition: MatrixFile.h:99
int m_height
Definition: MatrixFile.h:90
static size_t totalCount
Definition: MatrixFile.cpp:51
static QMutex m_createMutex
Definition: MatrixFile.h:102
mode_t m_fmode
Definition: MatrixFile.h:87
void getColumnAt(int x, void *data)
Definition: MatrixFile.cpp:266
void set(size_t column)
static size_t totalStorage
Definition: MatrixFile.cpp:50
int m_headerSize
Definition: MatrixFile.h:91
bool isAllOn() const
Profile point instance class.
Definition: Profiler.h:86