2015-08-06 18 views
7

Tôi thấy một tình huống lạ với bộ đệm đầu ra nhỏ với Java 8u45 và phương pháp java.util.Deflater.deflate(byte[] b, int off, int len, int flush) khi được sử dụng với bộ đệm đầu ra nhỏ.Deflater.deflate và bộ đệm đầu ra nhỏ

(Tôi đang làm việc trên một số mã mạng ở mức độ thấp liên quan đến mở rộng permessage-deflate sắp tới WebSocket của, bộ đệm quá nhỏ đang trở thành hiện thực đối với tôi)

Đoạn mã ví dụ:

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(); 
    } 
} 

Trong trường hợp trên , Tôi đang cố gắng tạo ra các byte nén cho đầu vào "Hello" bằng cách sử dụng một bộ đệm đầu ra có chiều dài 5 byte.

tôi sẽ giả định các byte kết quả sau:

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

nào dịch là

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

Tuy nhiên, khi Deflater.deflate() được sử dụng với một bộ đệm nhỏ, vòng bình thường này tiếp tục vô hạn ở mức 5 byte nén dữ liệu (dường như chỉ hiển thị ở bộ đệm 5 byte trở xuống).

Kết quả đầu ra của chạy demo trên ...

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) 

Nếu bạn thực hiện các đầu vào/đầu ra lớn hơn 5 byte thì vấn đề dường như biến mất. (Chỉ cần chắc chuỗi đầu vào "Hellox" để kiểm tra này cho chính mình)

Kết quả của dung dịch đệm 6 byte (đầu vào như "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 

Ngay cả những kết quả này có chút kỳ quặc đối với tôi, vì nó dường như có 2 deflate chuỗi byte-byte hiện nay. Vì vậy, tôi đoán câu hỏi cuối cùng của tôi là, tôi thiếu một cái gì đó về việc sử dụng Deflater mà làm điều kỳ lạ đối với tôi, hoặc là điều này chỉ vào một lỗi có thể có trong việc thực hiện JVM Deflater chính nó?

Cập nhật: ngày 07 tháng 8 năm 2015

Phát hiện này đã được chấp nhận như bugs.java.com/JDK-8133170

Trả lời

5

Đây là một zlib "tính năng", tài liệu trong zlib.h:

Trong trường hợp của Z_FULL_FLUSH hoặc Z_SYNC_FLUSH, đảm bảo rằng avail_out lớn hơn sáu để tránh các dấu đánh dấu lặp lại do avail_out == 0 khi trả về.

Điều gì đang xảy ra là mỗi cuộc gọi của deflate() với Z_SYNC_FLUSH đang chèn dấu năm dòng tuôn ra. Vì bạn không cung cấp đủ không gian đầu ra để có được điểm đánh dấu, bạn gọi lại để có thêm đầu ra, nhưng yêu cầu nó chèn một điểm đánh dấu tuôn ra khác cùng một lúc.

Những gì bạn cần phải làm là gọi deflate() với Z_SYNC_FLUSHlần, và sau đó nhận được tất cả các đầu ra có sẵn với thêm deflate() cuộc gọi, nếu cần thiết, sử dụng Z_NO_FLUSH (hoặc NO_FLUSH trong Java).

+0

Dang! Mark Adler trả lời câu hỏi của tôi, thật khó để có được câu trả lời có thẩm quyền hơn thế này. :-) –

+1

Đoán tôi nên gửi một lỗi apidoc với Oracle để thêm miếng ngon này vào tài liệu Deflater. Có vẻ hữu ích để biết. –

+0

Nó nên được coi là một "lỗi", phải không? Tại sao zlib làm điều đó? Để lưu cờ? – ZhongYu

Các vấn đề liên quan