Commit e5edf7f667daef8d5e6cee6394601719feac5328
1 parent
f2f88b8c
Introduce a QIODevice subclass for compressing output in fixed sized blocks
Showing
2 changed files
with
167 additions
and
0 deletions
openbr/core/qtutils.cpp
| ... | ... | @@ -500,6 +500,140 @@ QString getAbsolutePath(const QString &filename) |
| 500 | 500 | return QFileInfo(filename).absoluteFilePath(); |
| 501 | 501 | } |
| 502 | 502 | |
| 503 | +BlockCompression::BlockCompression(QIODevice *_basis) | |
| 504 | +{ | |
| 505 | + blockSize = 1000000; | |
| 506 | + setBasis(_basis); | |
| 507 | +} | |
| 508 | + | |
| 509 | +BlockCompression::BlockCompression() { blockSize = 1000000; }; | |
| 510 | + | |
| 511 | + | |
| 512 | +bool BlockCompression::open(QIODevice::OpenMode mode) | |
| 513 | +{ | |
| 514 | + this->setOpenMode(mode); | |
| 515 | + bool res = basis->open(mode); | |
| 516 | +// qDebug() << "basis: " << basis->isReadable() << " write:" << basis->isWritable(); | |
| 517 | + if (!res) | |
| 518 | + return false; | |
| 519 | + | |
| 520 | + blockReader.setDevice(basis); | |
| 521 | + blockWriter.setDevice(basis); | |
| 522 | + | |
| 523 | + if (mode & QIODevice::WriteOnly) { | |
| 524 | + precompressedBlockWriter = new QBuffer; | |
| 525 | + precompressedBlockWriter->open(QIODevice::ReadWrite); | |
| 526 | + } | |
| 527 | + else if (mode & QIODevice::ReadOnly) { | |
| 528 | +// qDebug() << "BLock reader status:" << blockReader.status(); | |
| 529 | + QByteArray compressedBlock; | |
| 530 | + blockReader >> compressedBlock; | |
| 531 | +// qDebug() <<" Post read attempt, " << blockReader.status(); | |
| 532 | + | |
| 533 | + decompressedBlock = qUncompress(compressedBlock); | |
| 534 | +// qDebug() << "Read compressed block: " << compressedBlock.size() << "Expanded to:" << decompressedBlock.size(); | |
| 535 | + decompressedBlockReader.setBuffer(&decompressedBlock); | |
| 536 | + decompressedBlockReader.open(QIODevice::ReadOnly); | |
| 537 | + } | |
| 538 | + | |
| 539 | + return true; | |
| 540 | +} | |
| 541 | + | |
| 542 | +void BlockCompression::close() | |
| 543 | +{ | |
| 544 | + // flush output buffer | |
| 545 | + if ((openMode() & QIODevice::WriteOnly) && precompressedBlockWriter) { | |
| 546 | + qDebug() << "Serializing final block"; | |
| 547 | + QByteArray compressedBlock = qCompress(precompressedBlockWriter->buffer(), -1); | |
| 548 | + blockWriter << compressedBlock; | |
| 549 | + qDebug() << "Done"; | |
| 550 | + } | |
| 551 | + basis->close(); | |
| 552 | +} | |
| 553 | + | |
| 554 | +void BlockCompression::setBasis(QIODevice *_basis) | |
| 555 | +{ | |
| 556 | + basis = _basis; | |
| 557 | + blockReader.setDevice(basis); | |
| 558 | + blockWriter.setDevice(basis); | |
| 559 | +} | |
| 560 | + | |
| 561 | +// read from current decompressed block, if out of space, read and decompress another | |
| 562 | +// block from basis | |
| 563 | +qint64 BlockCompression::readData(char *data, qint64 remaining) | |
| 564 | +{ | |
| 565 | +// qDebug() <<" Reading: " << remaining; | |
| 566 | + qint64 read = 0; | |
| 567 | + while (remaining > 0) { | |
| 568 | + qint64 single_read = decompressedBlockReader.read(data, remaining); | |
| 569 | + if (single_read == -1) | |
| 570 | + qFatal("miss read"); | |
| 571 | +// single_read = 0; | |
| 572 | + | |
| 573 | + | |
| 574 | + remaining -= single_read; | |
| 575 | + read += single_read; | |
| 576 | + data += single_read; | |
| 577 | +// qDebug() << "Read " << single_read << " reamining: "<< remaining; | |
| 578 | + | |
| 579 | + // need a new block | |
| 580 | + if (remaining > 0) { | |
| 581 | + QByteArray compressedBlock; | |
| 582 | + blockReader >> compressedBlock; | |
| 583 | + if (compressedBlock.size() == 0) { | |
| 584 | + return read; | |
| 585 | + } | |
| 586 | + decompressedBlock = qUncompress(compressedBlock); | |
| 587 | + | |
| 588 | + decompressedBlockReader.close(); | |
| 589 | + decompressedBlockReader.setBuffer(&decompressedBlock); | |
| 590 | + decompressedBlockReader.open(QIODevice::ReadOnly); | |
| 591 | + } | |
| 592 | + } | |
| 593 | + return blockReader.atEnd() && !basis->isReadable() ? -1 : read; | |
| 594 | +} | |
| 595 | + | |
| 596 | +bool BlockCompression::isSequential() const | |
| 597 | +{ | |
| 598 | + return true; | |
| 599 | +} | |
| 600 | + | |
| 601 | +qint64 BlockCompression::writeData(const char *data, qint64 remaining) | |
| 602 | +{ | |
| 603 | + qint64 written = 0; | |
| 604 | + | |
| 605 | + while (remaining > 0) { | |
| 606 | + // how much more can be put in this buffer? | |
| 607 | + qint64 capacity = blockSize - precompressedBlockWriter->pos(); | |
| 608 | + | |
| 609 | + // don't try to write beyond capacity | |
| 610 | + qint64 write_size = qMin(capacity, remaining); | |
| 611 | + | |
| 612 | + qint64 singleWrite = precompressedBlockWriter->write(data, write_size); | |
| 613 | + // ignore the error case here, we consdier basis's failure mode the real | |
| 614 | + // end case | |
| 615 | + if (singleWrite == -1) | |
| 616 | + singleWrite = 0; | |
| 617 | + | |
| 618 | + remaining -= singleWrite; | |
| 619 | + data += singleWrite; | |
| 620 | + written += singleWrite; | |
| 621 | + | |
| 622 | + if (remaining > 0) { | |
| 623 | + QByteArray compressedBlock = qCompress(precompressedBlockWriter->buffer(), -1); | |
| 624 | + | |
| 625 | + if (compressedBlock.size() != 0) | |
| 626 | + blockWriter << compressedBlock; | |
| 627 | + | |
| 628 | + delete precompressedBlockWriter; | |
| 629 | + precompressedBlockWriter = new QBuffer; | |
| 630 | + precompressedBlockWriter->open(QIODevice::ReadWrite); | |
| 631 | + } | |
| 632 | + } | |
| 633 | + return basis->isWritable() ? written : -1; | |
| 634 | +} | |
| 635 | + | |
| 636 | + | |
| 503 | 637 | |
| 504 | 638 | } // namespace QtUtils |
| 505 | 639 | ... | ... |
openbr/core/qtutils.h
| ... | ... | @@ -17,6 +17,7 @@ |
| 17 | 17 | #ifndef QTUTILS_QTUTILS_H |
| 18 | 18 | #define QTUTILS_QTUTILS_H |
| 19 | 19 | |
| 20 | +#include <QBuffer> | |
| 20 | 21 | #include <QByteArray> |
| 21 | 22 | #include <QDir> |
| 22 | 23 | #include <QFile> |
| ... | ... | @@ -93,6 +94,38 @@ namespace QtUtils |
| 93 | 94 | |
| 94 | 95 | /**** Rect Utilities ****/ |
| 95 | 96 | float overlap(const QRectF &r, const QRectF &s); |
| 97 | + | |
| 98 | + | |
| 99 | + class BlockCompression : public QIODevice | |
| 100 | + { | |
| 101 | + public: | |
| 102 | + BlockCompression(QIODevice *_basis); | |
| 103 | + BlockCompression(); | |
| 104 | + int blockSize; | |
| 105 | + QIODevice *basis; | |
| 106 | + | |
| 107 | + bool open(QIODevice::OpenMode mode); | |
| 108 | + | |
| 109 | + void close(); | |
| 110 | + | |
| 111 | + void setBasis(QIODevice *_basis); | |
| 112 | + | |
| 113 | + QDataStream blockReader; | |
| 114 | + QByteArray decompressedBlock; | |
| 115 | + QBuffer decompressedBlockReader; | |
| 116 | + | |
| 117 | + // read from current decompressed block, if out of space, read and decompress another | |
| 118 | + // block from basis | |
| 119 | + qint64 readData(char *data, qint64 remaining); | |
| 120 | + | |
| 121 | + bool isSequential() const; | |
| 122 | + | |
| 123 | + // write to a QByteArray, when max block sized is reached, compress and write | |
| 124 | + // it to basis | |
| 125 | + QBuffer * precompressedBlockWriter; | |
| 126 | + QDataStream blockWriter; | |
| 127 | + qint64 writeData(const char *data, qint64 remaining); | |
| 128 | + }; | |
| 96 | 129 | } |
| 97 | 130 | |
| 98 | 131 | #endif // QTUTILS_QTUTILS_H | ... | ... |