2015-08-06 12 views
7

Vedo una situazione strana con buffer di output di piccole dimensioni con Java 8u45 e il metodo java.util.Deflater.deflate(byte[] b, int off, int len, int flush) quando utilizzato con buffer di output di piccole dimensioni.Deflater.deflate e buffer di uscita di piccole dimensioni

(sto lavorando su un codice di rete a basso livello relative al prossimo permessage-deflate estensione di WebSocket, così piccoli buffer sono una realtà per me)

Il codice di esempio:

package deflate; 

import java.nio.charset.StandardCharsets; 
import java.util.zip.Deflater; 

public class DeflaterSmallBufferBug 
{ 
    public static void main(String[] args) 
    { 
     boolean nowrap = true; 
     Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION,nowrap); 

     byte[] input = "Hello".getBytes(StandardCharsets.UTF_8); 

     System.out.printf("input is %,d bytes - %s%n",input.length,getHex(input,0,input.length)); 

     deflater.setInput(input); 

     byte[] output = new byte[input.length]; 

     // break out of infinite loop seen with bug 
     int maxloops = 10; 

     // Compress the data 
     while (maxloops-- > 0) 
     { 
      int compressed = deflater.deflate(output,0,output.length,Deflater.SYNC_FLUSH); 
      System.out.printf("compressed %,d bytes - %s%n",compressed,getHex(output,0,compressed)); 

      if (compressed < output.length) 
      { 
       System.out.printf("Compress success"); 
       return; 
      } 
     } 

     System.out.printf("Exited compress (maxloops left %d)%n",maxloops); 
    } 

    private static String getHex(byte[] buf, int offset, int len) 
    { 
     StringBuilder hex = new StringBuilder(); 
     hex.append('['); 
     for (int i = offset; i < (offset + len); i++) 
     { 
      if (i > offset) 
      { 
       hex.append(' '); 
      } 
      hex.append(String.format("%02X",buf[i])); 
     } 
     hex.append(']'); 
     return hex.toString(); 
    } 
} 

Nel caso di cui sopra , Sto tentando di generare byte compressi per l'input "Hello" utilizzando un buffer di output di 5 byte di lunghezza.

Vorrei assumere i byte risultanti:

buffer 1 [ F2 48 CD C9 C9 ] 
buffer 2 [ 07 00 00 00 FF ] 
buffer 3 [ FF ] 

che si traduce come

[ F2 48 CD C9 C9 07 00 ] <-- the compressed data 
[ 00 00 FF FF ]   <-- the deflate tail bytes 

Tuttavia, quando Deflater.deflate() utilizzata con un piccolo tampone, questo ciclo normale continua all'infinito a 5 byte di compressa dati (sembra manifestarsi solo a buffer di 5 byte o inferiori).

Con conseguente uscita di eseguire il demo di cui sopra ...

input is 5 bytes - [48 65 6C 6C 6F] 
compressed 5 bytes - [F2 48 CD C9 C9] 
compressed 5 bytes - [07 00 00 00 FF] 
compressed 5 bytes - [FF 00 00 00 FF] 
compressed 5 bytes - [FF 00 00 00 FF] 
compressed 5 bytes - [FF 00 00 00 FF] 
compressed 5 bytes - [FF 00 00 00 FF] 
compressed 5 bytes - [FF 00 00 00 FF] 
compressed 5 bytes - [FF 00 00 00 FF] 
compressed 5 bytes - [FF 00 00 00 FF] 
compressed 5 bytes - [FF 00 00 00 FF] 
Exited compress (maxloops left -1) 

Se si effettua l'input/output maggiore di 5 byte, allora il problema sembra di andare via. (Basta fare la stringa di input "Hellox" per testare questo per te)

Risultati di fare il buffer 6 byte (ingresso come "Hellox")

input is 6 bytes - [48 65 6C 6C 6F 78] 
compressed 6 bytes - [F2 48 CD C9 C9 AF] 
compressed 6 bytes - [00 00 00 00 FF FF] 
compressed 5 bytes - [00 00 00 FF FF] 
Compress success 

Anche questi risultati sono po 'eccentrico per me, come sembra che ci sia 2 sgonfiare le sequenze di byte di coda presenti.

Quindi, suppongo che la mia ultima domanda sia, mi sto perdendo qualcosa sull'uso dello Deflater che mi rende strana, o punta forse a un possibile errore nell'implementazione JVM Deflater?

Aggiornamento: 7 agosto 2015

Questa scoperta è stata accettata come bugs.java.com/JDK-8133170

risposta

5

Questa è una zlib "caratteristica", documentata in zlib.h:

Nel caso di un Z_FULL_FLUSH o Z_SYNC_FLUSH, assicurarsi che avail_out sia maggiore di sei per evitare ripetuti indicatori di flush dovuti a avail_out == 0 al ritorno.

Quello che sta accadendo è che ogni chiamata di deflate() con Z_SYNC_FLUSH è l'inserimento di cinque byte marcatore a filo. Dal momento che non stai fornendo abbastanza spazio di output per ottenere il marker, richiami di nuovo per ottenere più output, ma ti chiedono di inserire un altro indicatore di flusso allo stesso tempo.

Cosa si dovrebbe fare è chiamare deflate() con Z_SYNC_FLUSHvolta, e quindi ottenere tutta la potenza disponibile con ulteriori deflate() chiamate, se necessario, che utilizzano Z_NO_FLUSH (o NO_FLUSH in Java).

+0

Dang! Mark Adler ha risposto alla mia domanda, difficile ottenere una risposta più autorevole di questo. :-) –

+1

Indovino che dovrei presentare un bug di apidoc con Oracle per aggiungere questo bocconcino alla documentazione di Deflater. Sembra utile saperlo. –

+0

Dovrebbe essere considerato un "bug", giusto? Perché zlib lo fa? Per salvare una bandiera? – ZhongYu

Problemi correlati