annotate data/fileio/BZipFileDevice.cpp @ 1798:13bd41bd8a17

Some work on making Model classes thread-safe in typical use - and documenting this. Some of the implementations are simpler now that EventSeries is thread-safe
author Chris Cannam
date Tue, 01 Oct 2019 11:22:48 +0100
parents 70e172e6cc59
children
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@148 7 This file copyright 2006 Chris Cannam.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #include "BZipFileDevice.h"
Chris@148 17
Chris@148 18 #include <bzlib.h>
Chris@148 19
Chris@148 20 #include <iostream>
Chris@148 21
Chris@843 22 #include "base/Debug.h"
Chris@843 23
Chris@1383 24 // for dup:
Chris@1381 25 #ifdef _MSC_VER
Chris@1381 26 #include <io.h>
Chris@1383 27 #else
Chris@1383 28 #include <unistd.h>
Chris@1381 29 #endif
Chris@1381 30
Chris@148 31 BZipFileDevice::BZipFileDevice(QString fileName) :
Chris@148 32 m_fileName(fileName),
Chris@1348 33 m_qfile(fileName),
Chris@1582 34 m_file(nullptr),
Chris@1582 35 m_bzFile(nullptr),
Chris@207 36 m_atEnd(true),
Chris@207 37 m_ok(true)
Chris@148 38 {
Chris@148 39 }
Chris@148 40
Chris@148 41 BZipFileDevice::~BZipFileDevice()
Chris@148 42 {
Chris@690 43 // SVDEBUG << "BZipFileDevice::~BZipFileDevice(" << m_fileName << ")" << endl;
Chris@148 44 if (m_bzFile) close();
Chris@148 45 }
Chris@148 46
Chris@148 47 bool
Chris@207 48 BZipFileDevice::isOK() const
Chris@207 49 {
Chris@207 50 return m_ok;
Chris@207 51 }
Chris@207 52
Chris@207 53 bool
Chris@148 54 BZipFileDevice::open(OpenMode mode)
Chris@148 55 {
Chris@203 56 setErrorString("");
Chris@203 57
Chris@148 58 if (m_bzFile) {
Chris@148 59 setErrorString(tr("File is already open"));
Chris@148 60 return false;
Chris@148 61 }
Chris@148 62
Chris@148 63 if (mode & Append) {
Chris@148 64 setErrorString(tr("Append mode not supported"));
Chris@207 65 m_ok = false;
Chris@148 66 return false;
Chris@148 67 }
Chris@148 68
Chris@148 69 if ((mode & (ReadOnly | WriteOnly)) == 0) {
Chris@148 70 setErrorString(tr("File access mode not specified"));
Chris@207 71 m_ok = false;
Chris@148 72 return false;
Chris@148 73 }
Chris@148 74
Chris@148 75 if ((mode & ReadOnly) && (mode & WriteOnly)) {
Chris@148 76 setErrorString(tr("Read and write modes both specified"));
Chris@207 77 m_ok = false;
Chris@148 78 return false;
Chris@148 79 }
Chris@148 80
Chris@1381 81 // This is all going to be a bit silly.
Chris@1381 82 //
Chris@1381 83 // We open the file with QFile so as not to have to worry about locale
Chris@1381 84 // support ourselves (especially on Windows). Then we get a fd from
Chris@1381 85 // QFile and "convert" it to a FILE* using fdopen because that is what
Chris@1381 86 // the bz2 library needs for reading and writing an already-open file.
Chris@1381 87 //
Chris@1381 88 // fdopen takes over the fd it is given, and will close it when fclose
Chris@1381 89 // is called. (We must call fclose, because it's needed to avoid
Chris@1381 90 // leaking the file stream structure.)
Chris@1381 91 //
Chris@1381 92 // But QFile will also close its fd, either when we call QFile::close
Chris@1381 93 // or on destruction -- there doesn't seem to be a way to avoid that
Chris@1381 94 // for a file that QFile opened.
Chris@1381 95 //
Chris@1381 96 // So we have to add an extra dup() in to the fdopen to avoid a double
Chris@1381 97 // close.
Chris@1381 98 //
Chris@1381 99 // Note that bz2 will *not* fclose the FILE* it was passed, so we
Chris@1381 100 // don't have a problem with calling both bzWriteClose and fclose.
Chris@1381 101
Chris@148 102 if (mode & WriteOnly) {
Chris@148 103
Chris@1348 104 if (!m_qfile.open(QIODevice::WriteOnly)) {
Chris@1348 105 setErrorString(tr("Failed to open file for writing"));
Chris@1348 106 m_ok = false;
Chris@1348 107 return false;
Chris@1348 108 }
Chris@1348 109
Chris@1381 110 m_file = fdopen(dup(m_qfile.handle()), "wb");
Chris@148 111 if (!m_file) {
Chris@1348 112 setErrorString(tr("Failed to open file handle for writing"));
Chris@1348 113 m_qfile.close();
Chris@207 114 m_ok = false;
Chris@148 115 return false;
Chris@148 116 }
Chris@148 117
Chris@148 118 int bzError = BZ_OK;
Chris@148 119 m_bzFile = BZ2_bzWriteOpen(&bzError, m_file, 9, 0, 0);
Chris@148 120
Chris@148 121 if (!m_bzFile) {
Chris@148 122 fclose(m_file);
Chris@1582 123 m_file = nullptr;
Chris@1348 124 m_qfile.close();
Chris@148 125 setErrorString(tr("Failed to open bzip2 stream for writing"));
Chris@207 126 m_ok = false;
Chris@148 127 return false;
Chris@148 128 }
Chris@148 129
Chris@843 130 // cerr << "BZipFileDevice: opened \"" << m_fileName << "\" for writing" << endl;
Chris@148 131
Chris@148 132 setErrorString(QString());
Chris@148 133 setOpenMode(mode);
Chris@148 134 return true;
Chris@148 135 }
Chris@148 136
Chris@148 137 if (mode & ReadOnly) {
Chris@148 138
Chris@1348 139 if (!m_qfile.open(QIODevice::ReadOnly)) {
Chris@1348 140 setErrorString(tr("Failed to open file for reading"));
Chris@1348 141 m_ok = false;
Chris@1348 142 return false;
Chris@1348 143 }
Chris@1348 144
Chris@1381 145 m_file = fdopen(dup(m_qfile.handle()), "rb");
Chris@148 146 if (!m_file) {
Chris@1348 147 setErrorString(tr("Failed to open file handle for reading"));
Chris@207 148 m_ok = false;
Chris@148 149 return false;
Chris@148 150 }
Chris@148 151
Chris@148 152 int bzError = BZ_OK;
Chris@1582 153 m_bzFile = BZ2_bzReadOpen(&bzError, m_file, 0, 0, nullptr, 0);
Chris@148 154
Chris@148 155 if (!m_bzFile) {
Chris@148 156 fclose(m_file);
Chris@1582 157 m_file = nullptr;
Chris@1348 158 m_qfile.close();
Chris@148 159 setErrorString(tr("Failed to open bzip2 stream for reading"));
Chris@207 160 m_ok = false;
Chris@148 161 return false;
Chris@148 162 }
Chris@148 163
Chris@843 164 // cerr << "BZipFileDevice: opened \"" << m_fileName << "\" for reading" << endl;
Chris@148 165
Chris@148 166 m_atEnd = false;
Chris@148 167
Chris@148 168 setErrorString(QString());
Chris@148 169 setOpenMode(mode);
Chris@148 170 return true;
Chris@148 171 }
Chris@148 172
Chris@148 173 setErrorString(tr("Internal error (open for neither read nor write)"));
Chris@207 174 m_ok = false;
Chris@148 175 return false;
Chris@148 176 }
Chris@148 177
Chris@148 178 void
Chris@148 179 BZipFileDevice::close()
Chris@148 180 {
Chris@148 181 if (!m_bzFile) {
Chris@148 182 setErrorString(tr("File not open"));
Chris@207 183 m_ok = false;
Chris@148 184 return;
Chris@148 185 }
Chris@148 186
Chris@148 187 int bzError = BZ_OK;
Chris@148 188
Chris@148 189 if (openMode() & WriteOnly) {
Chris@148 190 unsigned int in = 0, out = 0;
Chris@148 191 BZ2_bzWriteClose(&bzError, m_bzFile, 0, &in, &out);
Chris@1429 192 // cerr << "Wrote bzip2 stream (in=" << in << ", out=" << out << ")" << endl;
Chris@1381 193 if (bzError != BZ_OK) {
Chris@1381 194 setErrorString(tr("bzip2 stream write close error"));
Chris@1381 195 }
Chris@148 196 fclose(m_file);
Chris@1348 197 m_qfile.close();
Chris@1582 198 m_bzFile = nullptr;
Chris@1582 199 m_file = nullptr;
Chris@207 200 m_ok = false;
Chris@148 201 return;
Chris@148 202 }
Chris@148 203
Chris@148 204 if (openMode() & ReadOnly) {
Chris@148 205 BZ2_bzReadClose(&bzError, m_bzFile);
Chris@148 206 if (bzError != BZ_OK) {
Chris@148 207 setErrorString(tr("bzip2 stream read close error"));
Chris@148 208 }
Chris@148 209 fclose(m_file);
Chris@1348 210 m_qfile.close();
Chris@1582 211 m_bzFile = nullptr;
Chris@1582 212 m_file = nullptr;
Chris@207 213 m_ok = false;
Chris@148 214 return;
Chris@148 215 }
Chris@148 216
Chris@148 217 setErrorString(tr("Internal error (close for neither read nor write)"));
Chris@148 218 return;
Chris@148 219 }
Chris@148 220
Chris@148 221 qint64
Chris@148 222 BZipFileDevice::readData(char *data, qint64 maxSize)
Chris@148 223 {
Chris@148 224 if (m_atEnd) return 0;
Chris@148 225
Chris@148 226 int bzError = BZ_OK;
Chris@1038 227 int read = BZ2_bzRead(&bzError, m_bzFile, data, int(maxSize));
Chris@148 228
Chris@690 229 // SVDEBUG << "BZipFileDevice::readData: requested " << maxSize << ", read " << read << endl;
Chris@148 230
Chris@148 231 if (bzError != BZ_OK) {
Chris@148 232 if (bzError != BZ_STREAM_END) {
Chris@843 233 cerr << "BZipFileDevice::readData: error condition" << endl;
Chris@148 234 setErrorString(tr("bzip2 stream read error"));
Chris@207 235 m_ok = false;
Chris@148 236 return -1;
Chris@148 237 } else {
Chris@690 238 // SVDEBUG << "BZipFileDevice::readData: reached end of file" << endl;
Chris@148 239 m_atEnd = true;
Chris@148 240 }
Chris@148 241 }
Chris@148 242
Chris@148 243 return read;
Chris@148 244 }
Chris@148 245
Chris@148 246 qint64
Chris@148 247 BZipFileDevice::writeData(const char *data, qint64 maxSize)
Chris@148 248 {
Chris@148 249 int bzError = BZ_OK;
Chris@1038 250 BZ2_bzWrite(&bzError, m_bzFile, (void *)data, int(maxSize));
Chris@148 251
Chris@690 252 // SVDEBUG << "BZipFileDevice::writeData: " << maxSize << " to write" << endl;
Chris@148 253
Chris@148 254 if (bzError != BZ_OK) {
Chris@843 255 cerr << "BZipFileDevice::writeData: error condition" << endl;
Chris@148 256 setErrorString("bzip2 stream write error");
Chris@207 257 m_ok = false;
Chris@148 258 return -1;
Chris@148 259 }
Chris@148 260
Chris@690 261 // SVDEBUG << "BZipFileDevice::writeData: wrote " << maxSize << endl;
Chris@148 262
Chris@148 263 return maxSize;
Chris@148 264 }
Chris@148 265