5

インターネットに隠されている ByteBuffers に関して gzip-deflating 用のライブラリはありますか? 生データをプッシュしてからデフレートされたデータをプルできるものはありますか? 検索しましたが、InputStreams と OutputStreams を扱うライブラリしか見つかりませんでした。

パイプライン アーキテクチャで ByteBuffers のフローを圧縮するための gzip フィルターを作成する必要があります。これは、最後の要素が前の要素からデータをプルするプル アーキテクチャです。私たちの gzip フィルターは ByteBuffers のフローを処理します。利用可能な単一の Stream オブジェクトはありません。

データ フローをある種の InputStream として適応させ、GZipOutputStream を使用して要件を満たしましたが、アダプタ コードの量は控えめに言っても面倒です。

承認後の編集: 記録として、私たちのアーキテクチャは GStreamer などのアーキテクチャに似ています。

4

3 に答える 3

5

「インターネットに隠されている」部分はわかりませんが、zlibはメモリ内のgzip形式の圧縮と解凍を行います。java.util.zipAPIは、制限はありますが、zlibへのアクセスを提供します。インターフェイスの制限により、zlibがgzipストリームを直接生成および消費するように要求することはできません。ただし、このnowrapオプションを使用して、生の収縮データを生成および消費することができます。CRC32次に、のクラスを使用して、独自のgzipヘッダーとトレーラーを簡単にロールできますjava.util.zip。固定の10バイトのヘッダーを付加し、4バイトのCRCを付加し、次に4バイトの非圧縮長(モジュロ2 32)をリトルエンディアンの順序で付加することができます。

于 2012-11-15T00:13:56.703 に答える
3

このアプローチを提案してくれた Mark Adler の功績は、私の元の回答よりもはるかに優れています。

package stack;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.zip.CRC32;
import java.util.zip.Deflater;

public class BufferDeflate2 {
    /** The standard 10 byte GZIP header */
    private static final byte[] GZIP_HEADER = new byte[] { 0x1f, (byte) 0x8b,
            Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 };

    /** CRC-32 of uncompressed data. */
    private final CRC32 crc = new CRC32();

    /** Deflater to deflate data */
    private final Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION,
            true);

    /** Output buffer building area */
    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    /** Internal transfer space */
    private final byte[] transfer = new byte[1000];

    /** The flush mode to use at the end of each buffer */
    private final int flushMode;


    /**
     * New buffer deflater
     * 
     * @param syncFlush
     *            if true, all data in buffer can be immediately decompressed
     *            from output buffer
     */
    public BufferDeflate2(boolean syncFlush) {
        flushMode = syncFlush ? Deflater.SYNC_FLUSH : Deflater.NO_FLUSH;
        buffer.write(GZIP_HEADER, 0, GZIP_HEADER.length);
    }


    /**
     * Deflate the buffer
     * 
     * @param in
     *            the buffer to deflate
     * @return deflated representation of the buffer
     */
    public ByteBuffer deflate(ByteBuffer in) {
        // convert buffer to bytes
        byte[] inBytes;
        int off = in.position();
        int len = in.remaining();
        if( in.hasArray() ) {
            inBytes = in.array();
        } else {
            off = 0;
            inBytes = new byte[len];
            in.get(inBytes);
        }

        // update CRC and deflater
        crc.update(inBytes, off, len);
        deflater.setInput(inBytes, off, len);

        while( !deflater.needsInput() ) {
            int r = deflater.deflate(transfer, 0, transfer.length, flushMode);
            buffer.write(transfer, 0, r);
        }

        byte[] outBytes = buffer.toByteArray();
        buffer.reset();
        return ByteBuffer.wrap(outBytes);
    }


    /**
     * Write the final buffer. This writes any remaining compressed data and the GZIP trailer.
     * @return the final buffer
     */
    public ByteBuffer doFinal() {
        // finish deflating
        deflater.finish();

        // write all remaining data
        int r;
        do {
            r = deflater.deflate(transfer, 0, transfer.length,
                    Deflater.FULL_FLUSH);
            buffer.write(transfer, 0, r);
        } while( r == transfer.length );

        // write GZIP trailer
        writeInt((int) crc.getValue());
        writeInt((int) deflater.getBytesRead());

        // reset deflater
        deflater.reset();

        // final output
        byte[] outBytes = buffer.toByteArray();
        buffer.reset();
        return ByteBuffer.wrap(outBytes);
    }


    /**
     * Write a 32 bit value in little-endian order
     * 
     * @param v
     *            the value to write
     */
    private void writeInt(int v) {
        System.out.println("v="+v);
        buffer.write(v & 0xff);
        buffer.write((v >> 8) & 0xff);
        buffer.write((v >> 16) & 0xff);
        buffer.write((v >> 24) & 0xff);
    }


    /**
     * For testing. Pass in the name of a file to GZIP compress
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        File inFile = new File(args[0]);
        File outFile = new File(args[0]+".test.gz");
        FileChannel inChan = (new FileInputStream(inFile)).getChannel();
        FileChannel outChan = (new FileOutputStream(outFile)).getChannel();

        BufferDeflate2 def = new BufferDeflate2(false);

        ByteBuffer buf = ByteBuffer.allocate(500);
        while( true ) {
            buf.clear();
            int r = inChan.read(buf);
            if( r==-1 ) break;
            buf.flip();
            ByteBuffer compBuf = def.deflate(buf);
            outChan.write(compBuf);
        }

        ByteBuffer compBuf = def.doFinal();
        outChan.write(compBuf);

        inChan.close();
        outChan.close();
    }
}
于 2012-11-15T19:47:25.263 に答える
1

ByteBuffer の処理は難しくありません。以下のサンプルコードを参照してください。バッファがどのように作成されるかを知る必要があります。オプションは次のとおりです。

  1. 各バッファは個別に圧縮されます。これは非常に扱いやすいので、そうではないと思います。バッファをバイト配列に変換し、それを GZIPInputStream 内の ByteArrayInputStream にラップするだけです。
  2. 各バッファはライターによって SYNC_FLUSH で終了されたため、ストリーム内のデータのブロック全体を構成します。ライターによってバッファーに書き込まれたすべてのデータは、リーダーによってすぐに読み取ることができます。
  3. 各バッファーは、GZIP ストリームの一部にすぎません。リーダーがバッファーから何かを読み取ることができるという保証はありません。

GZIP によって生成されたデータは、順番に処理する必要があります。ByteBuffers は、生成されたのと同じ順序で処理する必要があります。

サンプルコード:

package stack;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.Pipe;
import java.nio.channels.SelectableChannel;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPInputStream;

public class BufferDeflate {

    static AtomicInteger idSrc = new AtomicInteger(1);

    /** Queue for transferring buffers */
    final BlockingQueue<ByteBuffer> buffers = new LinkedBlockingQueue<ByteBuffer>();

    /** The entry point for deflated buffers */
    final Pipe.SinkChannel bufSink;

    /** The source for the inflater */
    final Pipe.SourceChannel infSource;

    /** The destination for the inflater */
    final Pipe.SinkChannel infSink;

    /** The source for the outside world */
    public final SelectableChannel source;



    class Relayer extends Thread {
        public Relayer(int id) {
            super("BufferRelayer" + id);
        }


        public void run() {
            try {
                while( true ) {
                    ByteBuffer buf = buffers.take();
                    if( buf != null ) {
                        bufSink.write(buf);
                    } else {
                        bufSink.close();
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }



    class Inflater extends Thread {
        public Inflater(int id) {
            super("BufferInflater" + id);
        }


        public void run() {
            try {
                InputStream in = Channels.newInputStream(infSource);
                GZIPInputStream gzip = new GZIPInputStream(in);
                OutputStream out = Channels.newOutputStream(infSink);

                int ch;
                while( (ch = gzip.read()) != -1 ) {
                    out.write(ch);
                }
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * New buffer inflater
     */
    public BufferDeflate() throws IOException {
        Pipe pipe = Pipe.open();
        bufSink = pipe.sink();
        infSource = pipe.source();

        pipe = Pipe.open();
        infSink = pipe.sink();
        source = pipe.source().configureBlocking(false);

        int id = idSrc.incrementAndGet();

        Thread thread = new Relayer(id);
        thread.setDaemon(true);
        thread.start();

        thread = new Inflater(id);
        thread.setDaemon(true);
        thread.start();
    }


    /**
     * Add the buffer to the stream. A null buffer closes the stream
     * 
     * @param buf
     *            the buffer to add
     * @throws IOException
     */
    public void add(ByteBuffer buf) throws IOException {
        buffers.offer(buf);
    }
}

メソッドにバッファを渡し、addパブリックsourceチャネルから読み取るだけです。特定のバイト数を処理した後に GZIP から読み取ることができるデータの量を予測することは不可能です。そのため、バイト バッファーを追加するスレッドと同じスレッドで安全に読み取ることができるように、sourceチャネルをノンブロッキングにしました。

于 2012-11-15T02:21:36.057 に答える