2016-07-01 13 views
1

Ich möchte in der Lage sein, eine gzip (.gz) -Datei unter Verwendung von gleichzeitigen CPU-Threads zu erzeugen. Das heißt, ich würde separate Blöcke aus der Eingabedatei mit separat initialisierten z_stream-Datensätzen entleeren.Erstellen eines gzip-Streams aus separat komprimierten Blöcken

Die resultierende Datei sollte von der zlib-Funktion inflate() in einer klassischen single-threaded-Operation gelesen werden können.

Ist das möglich? Auch wenn es angepassten Zlib-Code erfordert? Die einzige Voraussetzung wäre, dass der aktuell vorhandene zlib-Inflationscode damit umgehen könnte.

aktualisieren

Der pigz Quellcode zeigt, wie es funktioniert. Es nutzt einige ausgefeilte Optimierungen, um das Wörterbuch zwischen Chunks zu teilen und die Komprimierungsrate optimal zu halten. Es behandelt weiter das Bit-Packing, wenn eine neuere zlib-Version verwendet wird.

Wie auch immer, ich mag es zu verstehen, wie man meine eigenen, die Dinge einfach zu halten, ohne die Optimierungen pigz verwendet.

Und während viele Quellcode als die ultimative Dokumentation betrachten (Ed Post, anyone?) habe ich es eher in einfachen Worten erklärt, um Missverständnisse zu vermeiden. (. Während die Dokumente tatsächlich beschreiben, was ziemlich gut der Fall ist, sie nicht allzu gut erklären, was rollen getan werden muss, um eine eigene ist)

den Code aus Surfen, dachte ich, so weit dies viel aus:

Es scheint, dass man einfach jeden komprimierten Chunk unter Verwendung von deflate(..., Z_SYNC_FLUSH) anstelle von Z_FINISH erstellt. Allerdings gibt deflateEnd() einen Fehler dann nicht sicher, ob das ignoriert werden kann. Und man muss die letzte Checksumme manuell über alle Chunks berechnen, obwohl ich mich wundere, wie man die Checksumme am Ende hinzufügt. Es gibt auch eine ziemlich komplexe put_trailer()-Funktion zum Schreiben eines gzip-Headers - ich frage mich, ob das auch mit zlibs eigenem Code für einfache Fälle gehandhabt werden könnte?

Jede diesbezügliche Klarstellung wird begrüßt.

Auch, Ich realisiere, dass ich enthalten hätte fragen über das Schreiben eines zlib-Stream auf die gleiche Weise, um Multithread-komprimierte Dateien in ein Zip-Archiv zu schreiben. Dort, vermute ich, sind weitere Vereinfachungen möglich, da der komplexere gzip-Header fehlt.

Antwort

3

Die Antwort in Ihrer Frage ist. Jeder Thread hat seine eigene deflate -Instanz zum Erzeugen von Roh-Deflate-Daten (siehe deflateInit2()), die den Chunk der ihm zugeführten Daten komprimiert und mit Z_SYNC_FLUSH anstelle von Z_FINISH endet. Bis auf den letzten Datenblock, den Sie mit einem Z_FINISH beenden. In jedem Fall wird dadurch jeder resultierende Strom komprimierter Daten an einer Bytegrenze beendet. Stellen Sie sicher, dass Sie alle generierten Daten aus deflate() erhalten. Dann können Sie alle komprimierten Datenströme verketten. (In der richtigen Reihenfolge!) Gehe mit einem gzip-Header vor, den du selbst erzeugst.Es ist trivial, das zu tun (siehe RFC 1952). Es kann nur eine konstante 10-Byte-Sequenz sein, wenn Sie keine zusätzlichen Informationen in der Kopfzeile benötigen (z. B. Dateiname, Änderungsdatum). Der gzip-Header ist nicht komplex.

Sie können auch die CRC-32 jedes unkomprimierten Chunks im selben Thread oder einem anderen Thread berechnen und diese CRC-32 mit crc32_combine() kombinieren. Das brauchst du für den gzip-Trailer.

Nachdem alle komprimierten Streams geschrieben wurden, die mit dem komprimierten Stream enden, der mit einem Z_FINISH beendet wurde, hängen Sie den Gzip-Trailer an. All dies ist der Vier-Byte-CRC-32 und die niedrigen vier Bytes der gesamten unkomprimierten Länge, beide in Little-Endian-Reihenfolge. Insgesamt acht Bytes.

In jedem Thread entweder Sie deflateEnd(), wenn sie mit jedem Chunk erfolgen können, oder wenn Sie Threads für mehr Stücke wiederverwenden, verwenden deflateReset(). Ich fand in Pigz, dass es viel effizienter ist, Threads offen zu lassen und deflate Instanzen in ihnen öffnen, wenn mehrere Chunks verarbeitet werden. Stellen Sie sicher, dass Sie deflateEnd() für den letzten Teilprozess verwenden, bevor Sie den Thread schließen. Ja, der Fehler von deflateEnd() kann ignoriert werden. Stellen Sie nur sicher, dass Sie deflate() ausgeführt haben, bis avail_out nicht Null ist, um alle komprimierten Daten zu erhalten.

Dadurch komprimiert jeder Thread seinen Chunk ohne Bezug zu anderen unkomprimierten Daten, wo solche Verweise normalerweise die Komprimierung verbessern würden, wenn es seriell geschieht. Wenn Sie weiter entwickelt werden möchten, können Sie jedem Thread den Teil der unkomprimierten Daten, die komprimiert werden sollen, und die letzten 32 KB des vorherigen Chunks zuführen, um den Verlauf für den Komprimierer bereitzustellen. Sie tun dies mit deflateSetDictionary().

Noch weiter fortgeschritten, können Sie die Anzahl der Bytes zwischen den komprimierten Streams eingefügt werden, indem Sie manchmal Z_PARTIAL_FLUSH 's bis zu einer Byte-Grenze. Siehe Pigz für die Details dazu.

Noch fortgeschrittener, aber langsamer, können Sie komprimierte Datenströme auf der Bit-Ebene anstelle der Byte-Ebene anfügen. Dies würde erfordern, jedes Byte des komprimierten Datenstroms zweimal zu verschieben, um einen neuen verschobenen Datenstrom zu erzeugen. Zumindest für sieben von acht vorhergehenden komprimierten Streams. Dies eliminiert alle zusätzlichen Bits, die zwischen komprimierten Strömen eingefügt sind.

Ein Zlib-Stream kann auf dieselbe Weise generiert werden, wobei adler32_combine() für die Prüfsumme verwendet wird.

Ihre Frage zu zlib impliziert eine Verwirrung. Das zip-Format verwendet nicht den zlib-Header und -Trailer. zip has its own structure, in dem eingebettete rohe deflate Ströme sind. Sie können den obigen Ansatz auch für diese unbearbeiteten Deflate-Streams verwenden.

+0

Ich versuche Deflate zu verwenden, indem ich meinen eigenen GZ Header und Trailer hinzufüge. Mit Setzen von Bit 4 in wbits beginnt die gz-Datei mit dem 10-Byte-Header '1F 8B 08 00 00 00 00 00 00 03', dann die deflationierten Daten '95 58 7B 6F D3 48 ...', endend mit CRC und Quelle Länge. So weit, ist es gut. Wenn das Bit 4 gelöscht ist, beginnt es mit "78 DA", dann mit den gleichen Daten "95 58 7B 6F D3 48 ..." und endet mit einer Prüfsumme (Adler?). Was machen die ersten 2 Bytes?Wenn ich nur den Header voranstelle und den CRC und den Ursprung hinzufüge, dekomprimiert er sich nicht als .gz-Datei. Wie generiere ich den reinen gz-Stream ohne Header und Trailer? –

+0

Ich denke, du meinst "Wie erzeuge ich einen defekten Deflate Stream?" Wenn es keinen Header und keinen Trailer gibt, dann ist es definitionsgemäß kein gzip- oder "gz" -Stream. Die Dokumentation in zlib.h sagt, wie man einen defekten Deflate-Stream erstellt. Sie geben 'deflateInit2()' einen negativen 'wbits' Wert. –

+0

Die ersten zwei Bytes des zlib-Streams identifizieren ihn als zlib-Stream und stellen die Komprimierungsmethode und die Fenstergröße bereit. Am Ende wird der Adler-32-Wert in Big-Endian-Reihenfolge gespeichert. (Im Gegensatz zum Gzip-Wrapper, wo die Trailer-Werte in Little-Endian-Reihenfolge gespeichert werden.) –

1

Sicher ..

http://zlib.net/pigz/

Eine parallele Implementierung von gzip für moderne Multi-Prozessor, Multi-Core-Maschinen

+0

Ordentlich. Jetzt, da ich bereits meinen eigenen Pthreads-basierten Code geschrieben habe, um die Standard-zlib-Funktionen zu verwenden, frage ich mich, ob Sie mir sagen können, welche Optionen ich verwenden muss, um dies mit deflateInit2 zu machen, oder ob diese Funktion eine neu geschriebene zlib benötigt , dh dass ich dafür putz benutzen muss? Wissen Sie? (Moment mal, lese jetzt die Dokumente in der Quelle ...) –

+0

Nein, ich weiß nicht, was deflateInit2 beinhaltet und habe das nicht bei der Programmierung benutzt. Aber ich weiß, dass Pigz-komprimierte Dateien mit gunzip dekomprimiert werden können, also müssen sie korrekt formatiert sein, damit gzip sie verarbeiten kann. – thelogix