📄 qwsdatabase.cpp
字号:
} unsigned char test = 0; while(!dbfile->atEnd()) { test = dbfile->getch(); uint size = readUint(); bool failed = FALSE; switch(test) { case type_record: if (i) i->insertDirect(readRecord(offset), offset); break; case type_free: case type_unknown: if (recoverFreeLists) { if (!eraseRecord(offset, TRUE)) stillErrors = TRUE; } break; default: // covers test_invalid. // something has gone wrong, stop reading if (truncate) { Global::truncateFile(dbfile->handle(), offset); dbfile->flush(); d->dbend = offset; } else { qWarning("WARNING, data inconsistency found, " "invalid element found while parsing records"); } failed = TRUE; break; } if (failed) break; offset += size + 1 + sizeof(uint); dbfile->at(offset); }}/*! Adds a new index \a i to the database.*/void QWSDatabase::addIndex(QWSDatabaseIndex* i){ d->indices.append(i);}/*! Removes index \a i from the database.*/void QWSDatabase::removeIndex(QWSDatabaseIndex* i){ d->indices.removeRef(i);}void QWSDatabase::removeRecordAt(uint pos, QWSDatabaseIndex* skip){ d->unindexify(pos,skip,this); if ( !skip ) eraseRecord( pos );}/*! Returns the record at offset \a pos in the database file. */QByteArray QWSDatabase::atOffset(uint pos) const{ return readRecord(pos);}/*! Sets indexing. If \a b is TRUE then all elements in the database will be inserted into the index, the order of elements being maintained. Otherwise elements inserted into the database will not be added to the index.*/void QWSDatabase::setIndexing(bool b){ if (d && d->indexed != b ) { d->indexed = b; if (b) reindex(); }}/*! Returns TRUE if the database is currently adding new items to the indexs. Otherwise returns FALSE.*/bool QWSDatabase::indexing() const{ if (d) return d->indexed; return FALSE;}/*! Returns the name of the attribute \a attr.*/QString QWSDatabase::attributeName(int attr) const{ if (!d) return QString::null; QString* s = d->keys[attr]; return s ? *s : QString::null;}/*! Returns the index of the attribute \a attr.*/int QWSDatabase::attribute(const QString &attr) const{ if (!d) return -1; QIntDictIterator<QString> it( d->keys ); // See QIntDictIterator for ( ; it.current(); ++it ) if(!qstrcmp(*(it.current()), attr)) return it.currentKey(); return -1;}/*! Adds a new attribute to the database with name \a attr at position \a id.*/void QWSDatabase::addAttribute(int id, const QString &attr){ if(!d) return; d->keys.replace(id, new QString(attr));}/*! \internal used to read a uint from the database.*/uint QWSDatabase::readUint() const{ uint v = 0; v += dbfile->getch(); v <<= 8; v += dbfile->getch(); v <<= 8; v += dbfile->getch(); v <<= 8; v += dbfile->getch(); return v;}/* return false on failure */bool QWSDatabase::writeUint(uint v){ uint mask = 0x000000FF; bool success = TRUE; if (dbfile->putch(v >> 24 & mask) == -1) success = FALSE; if (dbfile->putch(v >> 16 & mask) == -1) success = FALSE; if (dbfile->putch(v >> 8 & mask) == -1) success = FALSE; if (dbfile->putch(v & mask) == -1) success = FALSE; return success;}/*! \internal Attempts to write \a value at \a offset in the database file. If an error occurs the database will pop a warning querying the user if they want to retry or cancel the write.*/bool QWSDatabase::attemptWriteUint(uint offset, uint value){ while (1) { dbfile->resetStatus(); dbfile->at(offset); writeUint(value); dbfile->flush(); if (dbfile->status() == IO_Ok) break; if (recoverableError() == 1) return FALSE; } return TRUE;}/*! \internal Attempts to write \a size bytes of \a data at \a offset in the database file. If an error occurs the database will pop a warning querying the user if they want to retry or cancel the write.*/bool QWSDatabase::attemptWriteBlock(uint offset, const char *data, uint size) { dbfile->resetStatus(); while (1) { dbfile->at(offset); dbfile->writeBlock(data, size); dbfile->flush(); if (dbfile->status() == IO_Ok) break; if (recoverableError() == 1) return FALSE; dbfile->resetStatus(); } return TRUE;}/*! \internal This is a very important function. It attempts (just once) to write the block type \a t into the database file. If an error occurs the database will pop a warning indicate that an unrecoverable error has occurred. The operation cannot be completed.*/bool QWSDatabase::attemptSetBlockType(unsigned char t) { dbfile->resetStatus(); dbfile->putch(t); dbfile->flush(); if (dbfile->status() != IO_Ok) { unrecoverableError(); // tell the user return FALSE; } return TRUE;}void QWSDatabase::updateDirtCount(){ // now update the dirtCount; dirty++; dbfile->at(0); writeUint(dirty); dbfile->flush(); // this is at the start of the file. IF this fails, I don't // think we can recover.}/*! Attempts to write record \a r into the database. If successful, returns the offset where the record was written. Otherwise returns 0. \sa readRecord(), eraseRecord */uint QWSDatabase::writeRecord(const QByteArray &r){ QByteArray ba = r; uint real_size = ba.size() + 1 + sizeof(uint); /* ba now contains the data (and records it size) */ /* first check the free lists */ uint list_num = 0; do { if ( (d->freelists[list_num].count > 0) && (d->freelists[list_num].size >= real_size) ) break; list_num++; } while(list_num < number_lists); if (list_num == number_lists) list_num--; // list num is now the list with size just larger than real_size. // (or is the last list uint offset; uint size = 0; Q_UINT32 next = 0; if (d->freelists[list_num].count > 0) { // search the free list for the right size (first fit). offset = d->freelists[list_num].start; uint last = 0; uint count = d->freelists[list_num].count; bool found_gap = FALSE; while(count--) { dbfile->at(offset); unsigned char test = 0; test = dbfile->getch(); if(test != type_free) { qWarning("WARNING, data inconsistency found, free list element wasn't"); //recordError(test); // future break; // no free list found } size = readUint(); next = readUint(); if (size >= ba.size()) { if (last) { dbfile->at(last); if (!attemptSetBlockType(type_unknown)) return 0; // no harm done, yet if (!attemptWriteUint(last + 1 + sizeof(uint), next)) return 0; dbfile->at(last); if (!attemptSetBlockType(type_free)) return 0; // harm done, but can fix later } else { d->freelists[list_num].start = next; } ba.resize(size); // so erase gets the right amount of space d->freelists[list_num].count -= 1; found_gap = TRUE; break; } else { last = offset; offset = next; } } if (found_gap) { dbfile->at(offset); if (!attemptSetBlockType(type_unknown)) return 0; if (!attemptWriteBlock(offset + 1 + sizeof(uint), ba.data(), size)) return 0; // harm done but we will recover // mark as record dbfile->at(offset); if (!attemptSetBlockType(type_record)) return 0; // record is there... but we can't touch it. updateDirtCount(); return offset; } } // failed to find offset for whatever reason offset = d->dbend; d->dbend = offset + real_size; dbfile->at(offset); if (!attemptSetBlockType(type_invalid)) return 0; if (!attemptWriteUint(offset + 1, ba.size())) return 0; if (!attemptWriteBlock(offset + 1 + sizeof(uint), ba.data(), ba.size())) return 0; // mark as record. dbfile->at(offset); if (!attemptSetBlockType(type_record)) return 0; updateDirtCount(); return offset;}/*! Attempts to read a record from the database. If successful, returns the record at \a offset. Otherwise returns a the default record empty ByteArray. \sa writeRecord(), eraseRecord*/QByteArray QWSDatabase::readRecord(uint offset) const{ QByteArray ba; dbfile->at(offset); char test = dbfile->getch(); if (test != type_record) { //qWarning("WARNING, data inconsistency found, record element wasn't a record"); return QByteArray(); // no record here } uint size = readUint(); ba.resize(size); dbfile->readBlock(ba.data(), size); return QByteArray(ba);}/*! Attempts to erase whatever is at \a offset in the database file. If \a ignore_type is FALSE then will fail if \a offset is not the beginning of a record. This function will return FALSE if there was an error in erasing the record. This doesn't indicate that the record wasn't erased, but rather that it is indeterminate whether the record was erased. If the record was successfully erased the function returns TRUE. \sa writeRecord(), readRecord*/bool QWSDatabase::eraseRecord(uint offset, bool ignore_type){ if(offset > d->dbend) return FALSE; dbfile->at(offset); unsigned char test = dbfile->getch(); if (test == type_invalid || (!ignore_type && test != type_record)) { qWarning("WARNING, data inconsistency found record marked" " for deletion not a record."); return FALSE; // um, shouldn't erase something that isn't a record. } QDataStream ds(dbfile); uint orig_size = readUint(); // logic in writeRecord attempts to assure this will always be a // multiple of smallest_list; uint erase_size = orig_size + 1 + sizeof(uint); /* do what is needed to mark this record as erased */ if (offset + erase_size >= d->dbend) { Global::truncateFile(dbfile->handle(), offset); dbfile->flush(); d->dbend = offset; return TRUE; } uint list_num = 0; do { if ( (d->freelists[list_num].count > 0) && (d->freelists[list_num].size >= erase_size) ) break; list_num++; } while(list_num < number_lists); if (list_num == number_lists) list_num--; // list num is now the list with size just larger than real_size. // (or is the last list dbfile->at(offset); if (!attemptSetBlockType(type_unknown)) return FALSE; // we haven't modified anything yet. if (!attemptWriteUint(offset + 1 + sizeof(uint), d->freelists[list_num].start)) return FALSE; // its an unknonw block now, can fix later. // we assume this succeeds, no failure if it doesn't // now 0 out remaining space for (uint i = 0; i < (orig_size - 4); i++) dbfile->putch(0); dbfile->flush(); dbfile->at(offset); if (!attemptSetBlockType(type_free)) return FALSE; // we haven't modified anything yet. d->freelists[list_num].count += 1; d->freelists[list_num].start = offset; updateDirtCount(); return TRUE;}void QWSDatabase::reindex(){ d->reindex();}/*! Returns the dirt count of the database. This is useful for classes that store separate data to be sure they are still in sync with the database.*/uint QWSDatabase::dirtCount() const{ return dirty;}void QWSDatabase::writeHeader(){ hdrfile->open(IO_WriteOnly); QDataStream ds(hdrfile); ds << dirty; ds << d->dbend; uchar key_count = d->keys.count(); ds << key_count; QIntDictIterator<QString> it( d->keys ); // See QIntDictIterator for ( ; it.current(); ++it ) { uchar key = it.currentKey(); const char *name = it.current()->utf8(); ds << key; ds << name; } // the free lists uint ui; for(ui = 0; ui < number_lists; ui++) { ds << d->freelists[ui].start; ds << d->freelists[ui].count; } hdrfile->close();}bool QWSDatabase::readHeader(){ if (!d) return FALSE; if (!hdrfile->exists()) { d->dbend = 4; // after all first 4 bytes are a dirtcount // everything else is initialized already, or at least should be. writeHeader(); } else { hdrfile->open(IO_ReadOnly); QDataStream ds(hdrfile); uint tmp_dirty; ds >> tmp_dirty; if (tmp_dirty != dirty) { /* some problem has occurred in storing the header. Attempt to recover the free lists */ recover(0, TRUE, TRUE); hdrfile->close(); return TRUE; } d->dbend = 0; ds >> d->dbend; uchar key_count = 0; ds >> key_count; while(key_count-- > 0) { uchar key = 0; char *keyName; ds >> key; ds >> keyName; d->keys.replace(key, new QString(QString::fromUtf8(keyName))); } for(uint i = 0; i < number_lists; i++) { uint start = 0; uint end = 0; ds >> start; ds >> end; d->freelists[i].start = start; d->freelists[i].count = end; } hdrfile->close(); } return TRUE;}int QWSDatabase::recoverableError() const{ // FIXME This message probably needs to say more to the user. return QMessageBox::critical(0, tr("Could not write data."), tr("Could not write data."), tr("retry"), tr("cancel"));}void QWSDatabase::unrecoverableError() const{ // FIXME This message probably needs to say more to the user. QMessageBox::critical(0, tr("Fatal Storage Error"), tr("Could not write data."), tr("cancel"));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -