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,6 +500,140 @@ QString getAbsolutePath(const QString &filename) | ||
| 500 | return QFileInfo(filename).absoluteFilePath(); | 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 | } // namespace QtUtils | 638 | } // namespace QtUtils |
| 505 | 639 |
openbr/core/qtutils.h
| @@ -17,6 +17,7 @@ | @@ -17,6 +17,7 @@ | ||
| 17 | #ifndef QTUTILS_QTUTILS_H | 17 | #ifndef QTUTILS_QTUTILS_H |
| 18 | #define QTUTILS_QTUTILS_H | 18 | #define QTUTILS_QTUTILS_H |
| 19 | 19 | ||
| 20 | +#include <QBuffer> | ||
| 20 | #include <QByteArray> | 21 | #include <QByteArray> |
| 21 | #include <QDir> | 22 | #include <QDir> |
| 22 | #include <QFile> | 23 | #include <QFile> |
| @@ -93,6 +94,38 @@ namespace QtUtils | @@ -93,6 +94,38 @@ namespace QtUtils | ||
| 93 | 94 | ||
| 94 | /**** Rect Utilities ****/ | 95 | /**** Rect Utilities ****/ |
| 95 | float overlap(const QRectF &r, const QRectF &s); | 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 | #endif // QTUTILS_QTUTILS_H | 131 | #endif // QTUTILS_QTUTILS_H |