Mercurial > hg > svcore
comparison data/fileio/MatrixFile.cpp @ 148:1a42221a1522
* Reorganising code base. This revision will not compile.
author | Chris Cannam |
---|---|
date | Mon, 31 Jul 2006 11:49:58 +0000 |
parents | |
children | 4b2ea82fd0ed |
comparison
equal
deleted
inserted
replaced
147:3a13b0d4934e | 148:1a42221a1522 |
---|---|
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 Chris Cannam. | |
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 "base/System.h" | |
19 #include "base/Profiler.h" | |
20 #include "base/Exceptions.h" | |
21 | |
22 #include <sys/types.h> | |
23 #include <sys/stat.h> | |
24 #include <fcntl.h> | |
25 #include <unistd.h> | |
26 | |
27 #include <iostream> | |
28 | |
29 #include <cstdio> | |
30 #include <cassert> | |
31 | |
32 #include <QFileInfo> | |
33 #include <QDir> | |
34 | |
35 //#define DEBUG_MATRIX_FILE 1 | |
36 //#define DEBUG_MATRIX_FILE_READ_SET 1 | |
37 | |
38 #ifdef DEBUG_MATRIX_FILE_READ_SET | |
39 #define DEBUG_MATRIX_FILE 1 | |
40 #endif | |
41 | |
42 std::map<QString, int> MatrixFile::m_refcount; | |
43 QMutex MatrixFile::m_refcountMutex; | |
44 | |
45 MatrixFile::ResizeableBitsetMap MatrixFile::m_columnBitsets; | |
46 QMutex MatrixFile::m_columnBitsetWriteMutex; | |
47 | |
48 FileReadThread *MatrixFile::m_readThread = 0; | |
49 | |
50 static size_t totalStorage = 0; | |
51 static size_t totalMemory = 0; | |
52 static size_t totalCount = 0; | |
53 | |
54 MatrixFile::MatrixFile(QString fileBase, Mode mode, | |
55 size_t cellSize, bool eagerCache) : | |
56 m_fd(-1), | |
57 m_mode(mode), | |
58 m_flags(0), | |
59 m_fmode(0), | |
60 m_cellSize(cellSize), | |
61 m_width(0), | |
62 m_height(0), | |
63 m_headerSize(2 * sizeof(size_t)), | |
64 m_defaultCacheWidth(1024), | |
65 m_prevX(0), | |
66 m_eagerCache(eagerCache), | |
67 m_requestToken(-1), | |
68 m_spareData(0), | |
69 m_columnBitset(0) | |
70 { | |
71 Profiler profiler("MatrixFile::MatrixFile", true); | |
72 | |
73 if (!m_readThread) { | |
74 m_readThread = new FileReadThread; | |
75 m_readThread->start(); | |
76 } | |
77 | |
78 m_cache.data = 0; | |
79 | |
80 QDir tempDir(TempDirectory::getInstance()->getPath()); | |
81 QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase))); | |
82 bool newFile = !QFileInfo(fileName).exists(); | |
83 | |
84 if (newFile && m_mode == ReadOnly) { | |
85 std::cerr << "ERROR: MatrixFile::MatrixFile: Read-only mode " | |
86 << "specified, but cache file does not exist" << std::endl; | |
87 throw FileNotFound(fileName); | |
88 } | |
89 | |
90 if (!newFile && m_mode == ReadWrite) { | |
91 std::cerr << "Note: MatrixFile::MatrixFile: Read/write mode " | |
92 << "specified, but file already exists; falling back to " | |
93 << "read-only mode" << std::endl; | |
94 m_mode = ReadOnly; | |
95 } | |
96 | |
97 if (!eagerCache && m_mode == ReadOnly) { | |
98 std::cerr << "WARNING: MatrixFile::MatrixFile: Eager cacheing not " | |
99 << "specified, but file is open in read-only mode -- cache " | |
100 << "will not be used" << std::endl; | |
101 } | |
102 | |
103 m_flags = 0; | |
104 m_fmode = S_IRUSR | S_IWUSR; | |
105 | |
106 if (m_mode == ReadWrite) { | |
107 m_flags = O_RDWR | O_CREAT; | |
108 } else { | |
109 m_flags = O_RDONLY; | |
110 } | |
111 | |
112 #ifdef DEBUG_MATRIX_FILE | |
113 std::cerr << "MatrixFile::MatrixFile: opening " << fileName.toStdString() << "..." << std::endl; | |
114 #endif | |
115 | |
116 if ((m_fd = ::open(fileName.toLocal8Bit(), m_flags, m_fmode)) < 0) { | |
117 ::perror("Open failed"); | |
118 std::cerr << "ERROR: MatrixFile::MatrixFile: " | |
119 << "Failed to open cache file \"" | |
120 << fileName.toStdString() << "\""; | |
121 if (m_mode == ReadWrite) std::cerr << " for writing"; | |
122 std::cerr << std::endl; | |
123 throw FailedToOpenFile(fileName); | |
124 } | |
125 | |
126 if (newFile) { | |
127 resize(0, 0); // write header | |
128 } else { | |
129 size_t header[2]; | |
130 if (::read(m_fd, header, 2 * sizeof(size_t)) < 0) { | |
131 perror("Read failed"); | |
132 std::cerr << "ERROR: MatrixFile::MatrixFile: " | |
133 << "Failed to read header (fd " << m_fd << ", file \"" | |
134 << fileName.toStdString() << "\")" << std::endl; | |
135 throw FileReadFailed(fileName); | |
136 } | |
137 m_width = header[0]; | |
138 m_height = header[1]; | |
139 seekTo(0, 0); | |
140 } | |
141 | |
142 m_fileName = fileName; | |
143 | |
144 m_columnBitsetWriteMutex.lock(); | |
145 | |
146 if (m_columnBitsets.find(m_fileName) == m_columnBitsets.end()) { | |
147 m_columnBitsets[m_fileName] = new ResizeableBitset; | |
148 } | |
149 m_columnBitset = m_columnBitsets[m_fileName]; | |
150 | |
151 m_columnBitsetWriteMutex.unlock(); | |
152 | |
153 QMutexLocker locker(&m_refcountMutex); | |
154 ++m_refcount[fileName]; | |
155 | |
156 std::cerr << "MatrixFile(" << this << "): fd " << m_fd << ", file " << fileName.toStdString() << ", ref " << m_refcount[fileName] << std::endl; | |
157 | |
158 // std::cerr << "MatrixFile::MatrixFile: Done, size is " << "(" << m_width << ", " << m_height << ")" << std::endl; | |
159 | |
160 ++totalCount; | |
161 | |
162 } | |
163 | |
164 MatrixFile::~MatrixFile() | |
165 { | |
166 char *requestData = 0; | |
167 | |
168 if (m_requestToken >= 0) { | |
169 FileReadThread::Request request; | |
170 if (m_readThread->getRequest(m_requestToken, request)) { | |
171 requestData = request.data; | |
172 } | |
173 m_readThread->cancel(m_requestToken); | |
174 } | |
175 | |
176 if (requestData) free(requestData); | |
177 if (m_cache.data) free(m_cache.data); | |
178 if (m_spareData) free(m_spareData); | |
179 | |
180 if (m_fd >= 0) { | |
181 if (::close(m_fd) < 0) { | |
182 ::perror("MatrixFile::~MatrixFile: close failed"); | |
183 } | |
184 } | |
185 | |
186 if (m_fileName != "") { | |
187 | |
188 QMutexLocker locker(&m_refcountMutex); | |
189 | |
190 if (--m_refcount[m_fileName] == 0) { | |
191 | |
192 if (::unlink(m_fileName.toLocal8Bit())) { | |
193 ::perror("Unlink failed"); | |
194 std::cerr << "WARNING: MatrixFile::~MatrixFile: reference count reached 0, but failed to unlink file \"" << m_fileName.toStdString() << "\"" << std::endl; | |
195 } else { | |
196 std::cerr << "deleted " << m_fileName.toStdString() << std::endl; | |
197 } | |
198 | |
199 QMutexLocker locker2(&m_columnBitsetWriteMutex); | |
200 m_columnBitsets.erase(m_fileName); | |
201 delete m_columnBitset; | |
202 } | |
203 } | |
204 | |
205 totalStorage -= (m_headerSize + (m_width * m_height * m_cellSize)); | |
206 totalMemory -= (2 * m_defaultCacheWidth * m_height * m_cellSize); | |
207 totalCount --; | |
208 | |
209 std::cerr << "MatrixFile::~MatrixFile: " << std::endl; | |
210 std::cerr << "Total storage now " << totalStorage/1024 << "K, theoretical max memory " | |
211 << totalMemory/1024 << "K in " << totalCount << " instances" << std::endl; | |
212 | |
213 } | |
214 | |
215 void | |
216 MatrixFile::resize(size_t w, size_t h) | |
217 { | |
218 Profiler profiler("MatrixFile::resize", true); | |
219 | |
220 assert(m_mode == ReadWrite); | |
221 | |
222 QMutexLocker locker(&m_fdMutex); | |
223 | |
224 totalStorage -= (m_headerSize + (m_width * m_height * m_cellSize)); | |
225 totalMemory -= (2 * m_defaultCacheWidth * m_height * m_cellSize); | |
226 | |
227 off_t off = m_headerSize + (w * h * m_cellSize); | |
228 | |
229 #ifdef DEBUG_MATRIX_FILE | |
230 std::cerr << "MatrixFile::resize(" << w << ", " << h << "): resizing file" << std::endl; | |
231 #endif | |
232 | |
233 if (w * h < m_width * m_height) { | |
234 if (::ftruncate(m_fd, off) < 0) { | |
235 ::perror("WARNING: MatrixFile::resize: ftruncate failed"); | |
236 throw FileOperationFailed(m_fileName, "ftruncate"); | |
237 } | |
238 } | |
239 | |
240 m_width = 0; | |
241 m_height = 0; | |
242 | |
243 if (::lseek(m_fd, 0, SEEK_SET) == (off_t)-1) { | |
244 ::perror("ERROR: MatrixFile::resize: Seek to write header failed"); | |
245 throw FileOperationFailed(m_fileName, "lseek"); | |
246 } | |
247 | |
248 size_t header[2]; | |
249 header[0] = w; | |
250 header[1] = h; | |
251 if (::write(m_fd, header, 2 * sizeof(size_t)) != 2 * sizeof(size_t)) { | |
252 ::perror("ERROR: MatrixFile::resize: Failed to write header"); | |
253 throw FileOperationFailed(m_fileName, "write"); | |
254 } | |
255 | |
256 if (w > 0 && m_defaultCacheWidth > w) { | |
257 m_defaultCacheWidth = w; | |
258 } | |
259 | |
260 static size_t maxCacheMB = 16; | |
261 if (2 * m_defaultCacheWidth * h * m_cellSize > maxCacheMB * 1024 * 1024) { //!!! | |
262 m_defaultCacheWidth = (maxCacheMB * 1024 * 1024) / (2 * h * m_cellSize); | |
263 if (m_defaultCacheWidth < 16) m_defaultCacheWidth = 16; | |
264 } | |
265 | |
266 if (m_columnBitset) { | |
267 QMutexLocker locker(&m_columnBitsetWriteMutex); | |
268 m_columnBitset->resize(w); | |
269 } | |
270 | |
271 if (m_cache.data) { | |
272 free(m_cache.data); | |
273 m_cache.data = 0; | |
274 } | |
275 | |
276 if (m_spareData) { | |
277 free(m_spareData); | |
278 m_spareData = 0; | |
279 } | |
280 | |
281 m_width = w; | |
282 m_height = h; | |
283 | |
284 totalStorage += (m_headerSize + (m_width * m_height * m_cellSize)); | |
285 totalMemory += (2 * m_defaultCacheWidth * m_height * m_cellSize); | |
286 | |
287 #ifdef DEBUG_MATRIX_FILE | |
288 std::cerr << "MatrixFile::resize(" << w << ", " << h << "): cache width " | |
289 << m_defaultCacheWidth << ", storage " | |
290 << (m_headerSize + w * h * m_cellSize) << ", mem " | |
291 << (2 * h * m_defaultCacheWidth * m_cellSize) << std::endl; | |
292 | |
293 std::cerr << "Total storage " << totalStorage/1024 << "K, theoretical max memory " | |
294 << totalMemory/1024 << "K in " << totalCount << " instances" << std::endl; | |
295 #endif | |
296 | |
297 seekTo(0, 0); | |
298 } | |
299 | |
300 void | |
301 MatrixFile::reset() | |
302 { | |
303 Profiler profiler("MatrixFile::reset", true); | |
304 | |
305 assert (m_mode == ReadWrite); | |
306 | |
307 if (m_eagerCache) { | |
308 void *emptyCol = calloc(m_height, m_cellSize); | |
309 for (size_t x = 0; x < m_width; ++x) setColumnAt(x, emptyCol); | |
310 free(emptyCol); | |
311 } | |
312 | |
313 if (m_columnBitset) { | |
314 QMutexLocker locker(&m_columnBitsetWriteMutex); | |
315 m_columnBitset->resize(m_width); | |
316 } | |
317 } | |
318 | |
319 void | |
320 MatrixFile::getColumnAt(size_t x, void *data) | |
321 { | |
322 // Profiler profiler("MatrixFile::getColumnAt"); | |
323 | |
324 // assert(haveSetColumnAt(x)); | |
325 | |
326 if (getFromCache(x, 0, m_height, data)) return; | |
327 | |
328 // Profiler profiler2("MatrixFile::getColumnAt (uncached)"); | |
329 | |
330 ssize_t r = 0; | |
331 | |
332 #ifdef DEBUG_MATRIX_FILE | |
333 std::cerr << "MatrixFile::getColumnAt(" << x << ")" | |
334 << ": reading the slow way"; | |
335 | |
336 if (m_requestToken >= 0 && | |
337 x >= m_requestingX && | |
338 x < m_requestingX + m_requestingWidth) { | |
339 | |
340 std::cerr << " (awaiting " << m_requestingX << ", " << m_requestingWidth << " from disk)"; | |
341 } | |
342 | |
343 std::cerr << std::endl; | |
344 #endif | |
345 | |
346 m_fdMutex.lock(); | |
347 | |
348 if (seekTo(x, 0)) { | |
349 r = ::read(m_fd, data, m_height * m_cellSize); | |
350 } | |
351 | |
352 m_fdMutex.unlock(); | |
353 | |
354 if (r < 0) { | |
355 ::perror("MatrixFile::getColumnAt: read failed"); | |
356 throw FileReadFailed(m_fileName); | |
357 } | |
358 | |
359 return; | |
360 } | |
361 | |
362 bool | |
363 MatrixFile::getFromCache(size_t x, size_t ystart, size_t ycount, void *data) | |
364 { | |
365 m_cacheMutex.lock(); | |
366 | |
367 if (!m_cache.data || x < m_cache.x || x >= m_cache.x + m_cache.width) { | |
368 bool left = (m_cache.data && x < m_cache.x); | |
369 m_cacheMutex.unlock(); | |
370 primeCache(x, left); // this doesn't take effect until a later callback | |
371 m_prevX = x; | |
372 return false; | |
373 } | |
374 | |
375 memcpy(data, | |
376 m_cache.data + m_cellSize * ((x - m_cache.x) * m_height + ystart), | |
377 ycount * m_cellSize); | |
378 | |
379 m_cacheMutex.unlock(); | |
380 | |
381 if (m_cache.x > 0 && x < m_prevX && x < m_cache.x + m_cache.width/4) { | |
382 primeCache(x, true); | |
383 } | |
384 | |
385 if (m_cache.x + m_cache.width < m_width && | |
386 x > m_prevX && | |
387 x > m_cache.x + (m_cache.width * 3) / 4) { | |
388 primeCache(x, false); | |
389 } | |
390 | |
391 m_prevX = x; | |
392 return true; | |
393 } | |
394 | |
395 void | |
396 MatrixFile::setColumnAt(size_t x, const void *data) | |
397 { | |
398 assert(m_mode == ReadWrite); | |
399 | |
400 #ifdef DEBUG_MATRIX_FILE_READ_SET | |
401 std::cerr << "MatrixFile::setColumnAt(" << x << ")" << std::endl; | |
402 #endif | |
403 | |
404 ssize_t w = 0; | |
405 bool seekFailed = false; | |
406 | |
407 m_fdMutex.lock(); | |
408 | |
409 if (seekTo(x, 0)) { | |
410 w = ::write(m_fd, data, m_height * m_cellSize); | |
411 } else { | |
412 seekFailed = true; | |
413 } | |
414 | |
415 m_fdMutex.unlock(); | |
416 | |
417 if (!seekFailed && w != ssize_t(m_height * m_cellSize)) { | |
418 ::perror("WARNING: MatrixFile::setColumnAt: write failed"); | |
419 throw FileOperationFailed(m_fileName, "write"); | |
420 } else if (seekFailed) { | |
421 throw FileOperationFailed(m_fileName, "seek"); | |
422 } else { | |
423 QMutexLocker locker(&m_columnBitsetWriteMutex); | |
424 m_columnBitset->set(x); | |
425 } | |
426 } | |
427 | |
428 void | |
429 MatrixFile::suspend() | |
430 { | |
431 QMutexLocker locker(&m_fdMutex); | |
432 QMutexLocker locker2(&m_cacheMutex); | |
433 | |
434 if (m_fd < 0) return; // already suspended | |
435 | |
436 #ifdef DEBUG_MATRIX_FILE | |
437 std::cerr << "MatrixFile(" << this << ":" << m_fileName.toStdString() << ")::suspend(): fd was " << m_fd << std::endl; | |
438 #endif | |
439 | |
440 if (m_requestToken >= 0) { | |
441 void *data = 0; | |
442 FileReadThread::Request request; | |
443 if (m_readThread->getRequest(m_requestToken, request)) { | |
444 data = request.data; | |
445 } | |
446 m_readThread->cancel(m_requestToken); | |
447 if (data) free(data); | |
448 m_requestToken = -1; | |
449 } | |
450 | |
451 if (m_cache.data) { | |
452 free(m_cache.data); | |
453 m_cache.data = 0; | |
454 } | |
455 | |
456 if (m_spareData) { | |
457 free(m_spareData); | |
458 m_spareData = 0; | |
459 } | |
460 | |
461 if (::close(m_fd) < 0) { | |
462 ::perror("WARNING: MatrixFile::suspend: close failed"); | |
463 throw FileOperationFailed(m_fileName, "close"); | |
464 } | |
465 | |
466 m_fd = -1; | |
467 } | |
468 | |
469 void | |
470 MatrixFile::resume() | |
471 { | |
472 if (m_fd >= 0) return; | |
473 | |
474 #ifdef DEBUG_MATRIX_FILE | |
475 std::cerr << "MatrixFile(" << this << ")::resume()" << std::endl; | |
476 #endif | |
477 | |
478 if ((m_fd = ::open(m_fileName.toLocal8Bit(), m_flags, m_fmode)) < 0) { | |
479 ::perror("Open failed"); | |
480 std::cerr << "ERROR: MatrixFile::resume: " | |
481 << "Failed to open cache file \"" | |
482 << m_fileName.toStdString() << "\""; | |
483 if (m_mode == ReadWrite) std::cerr << " for writing"; | |
484 std::cerr << std::endl; | |
485 throw FailedToOpenFile(m_fileName); | |
486 } | |
487 | |
488 std::cerr << "MatrixFile(" << this << ":" << m_fileName.toStdString() << ")::resume(): fd is " << m_fd << std::endl; | |
489 } | |
490 | |
491 void | |
492 MatrixFile::primeCache(size_t x, bool goingLeft) | |
493 { | |
494 // Profiler profiler("MatrixFile::primeCache"); | |
495 | |
496 #ifdef DEBUG_MATRIX_FILE_READ_SET | |
497 std::cerr << "MatrixFile::primeCache(" << x << ", " << goingLeft << ")" << std::endl; | |
498 #endif | |
499 | |
500 size_t rx = x; | |
501 size_t rw = m_defaultCacheWidth; | |
502 | |
503 size_t left = rw / 3; | |
504 if (goingLeft) left = (rw * 2) / 3; | |
505 | |
506 if (rx > left) rx -= left; | |
507 else rx = 0; | |
508 | |
509 if (rx + rw > m_width) rw = m_width - rx; | |
510 | |
511 if (!m_eagerCache) { | |
512 | |
513 size_t ti = 0; | |
514 | |
515 for (ti = 0; ti < rw; ++ti) { | |
516 if (!m_columnBitset->get(rx + ti)) break; | |
517 } | |
518 | |
519 #ifdef DEBUG_MATRIX_FILE | |
520 if (ti < rw) { | |
521 std::cerr << "eagerCache is false and there's a hole at " | |
522 << rx + ti << ", reducing rw from " << rw << " to " | |
523 << ti << std::endl; | |
524 } | |
525 #endif | |
526 | |
527 rw = std::min(rw, ti); | |
528 if (rw < 10 || rx + rw <= x) return; | |
529 } | |
530 | |
531 QMutexLocker locker(&m_cacheMutex); | |
532 | |
533 FileReadThread::Request request; | |
534 | |
535 if (m_requestToken >= 0 && | |
536 m_readThread->getRequest(m_requestToken, request)) { | |
537 | |
538 if (x >= m_requestingX && | |
539 x < m_requestingX + m_requestingWidth) { | |
540 | |
541 if (m_readThread->isReady(m_requestToken)) { | |
542 | |
543 if (!request.successful) { | |
544 std::cerr << "ERROR: MatrixFile::primeCache: Last request was unsuccessful" << std::endl; | |
545 throw FileReadFailed(m_fileName); | |
546 } | |
547 | |
548 #ifdef DEBUG_MATRIX_FILE_READ_SET | |
549 std::cerr << "last request is ready! (" << m_requestingX << ", "<< m_requestingWidth << ")" << std::endl; | |
550 #endif | |
551 | |
552 m_cache.x = (request.start - m_headerSize) / (m_height * m_cellSize); | |
553 m_cache.width = request.size / (m_height * m_cellSize); | |
554 | |
555 #ifdef DEBUG_MATRIX_FILE_READ_SET | |
556 std::cerr << "received last request: actual size is: " << m_cache.x << ", " << m_cache.width << std::endl; | |
557 #endif | |
558 | |
559 if (m_cache.data) { | |
560 if (m_spareData) free(m_spareData); | |
561 m_spareData = m_cache.data; | |
562 } | |
563 m_cache.data = request.data; | |
564 | |
565 m_readThread->done(m_requestToken); | |
566 m_requestToken = -1; | |
567 } | |
568 | |
569 // already requested something covering this area; wait for it | |
570 return; | |
571 } | |
572 | |
573 // the current request is no longer of any use | |
574 m_readThread->cancel(m_requestToken); | |
575 | |
576 // crude way to avoid leaking the data | |
577 while (!m_readThread->isCancelled(m_requestToken)) { | |
578 usleep(10000); | |
579 } | |
580 | |
581 #ifdef DEBUG_MATRIX_FILE_READ_SET | |
582 std::cerr << "cancelled " << m_requestToken << std::endl; | |
583 #endif | |
584 | |
585 if (m_spareData) free(m_spareData); | |
586 m_spareData = request.data; | |
587 m_readThread->done(m_requestToken); | |
588 | |
589 m_requestToken = -1; | |
590 } | |
591 | |
592 if (m_fd < 0) { | |
593 m_fdMutex.lock(); | |
594 if (m_fd < 0) resume(); | |
595 m_fdMutex.unlock(); | |
596 } | |
597 | |
598 request.fd = m_fd; | |
599 request.mutex = &m_fdMutex; | |
600 request.start = m_headerSize + rx * m_height * m_cellSize; | |
601 request.size = rw * m_height * m_cellSize; | |
602 request.data = (char *)realloc(m_spareData, rw * m_height * m_cellSize); | |
603 MUNLOCK(request.data, rw * m_height * m_cellSize); | |
604 m_spareData = 0; | |
605 | |
606 m_requestingX = rx; | |
607 m_requestingWidth = rw; | |
608 | |
609 int token = m_readThread->request(request); | |
610 #ifdef DEBUG_MATRIX_FILE_READ_SET | |
611 std::cerr << "MatrixFile::primeCache: request token is " | |
612 << token << " (x = [" << rx << "], w = [" << rw << "], left = [" << goingLeft << "])" << std::endl; | |
613 #endif | |
614 m_requestToken = token; | |
615 } | |
616 | |
617 bool | |
618 MatrixFile::seekTo(size_t x, size_t y) | |
619 { | |
620 if (m_fd < 0) resume(); | |
621 | |
622 off_t off = m_headerSize + (x * m_height + y) * m_cellSize; | |
623 | |
624 if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) { | |
625 ::perror("Seek failed"); | |
626 std::cerr << "ERROR: MatrixFile::seekTo(" << x << ", " << y | |
627 << ") failed" << std::endl; | |
628 return false; | |
629 } | |
630 | |
631 return true; | |
632 } | |
633 |