13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/* 23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2014 Square, Inc. 33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License. 63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at 73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software 113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and 143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License. 153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage okio; 173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.EOFException; 193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException; 203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.DataFormatException; 213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.zip.Inflater; 223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/** 243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * A source that uses <a href="http://tools.ietf.org/html/rfc1951">DEFLATE</a> 253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * to decompress data read from another source. 263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic final class InflaterSource implements Source { 283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final BufferedSource source; 293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final Inflater inflater; 303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * When we call Inflater.setInput(), the inflater keeps our byte array until 333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * it needs input again. This tracks how many bytes the inflater is currently 343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * holding on to. 353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private int bufferBytesHeldByInflater; 373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private boolean closed; 383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public InflaterSource(Source source, Inflater inflater) { 403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this(Okio.buffer(source), inflater); 413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * This package-private constructor shares a buffer with its trusted caller. 453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * In general we can't share a BufferedSource because the inflater holds input 463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * bytes until they are inflated. 473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller InflaterSource(BufferedSource source, Inflater inflater) { 493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (source == null) throw new IllegalArgumentException("source == null"); 503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (inflater == null) throw new IllegalArgumentException("inflater == null"); 513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.source = source; 523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.inflater = inflater; 533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public long read( 563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller OkBuffer sink, long byteCount) throws IOException { 573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); 583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) throw new IllegalStateException("closed"); 593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (byteCount == 0) return 0; 603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller while (true) { 623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller boolean sourceExhausted = refill(); 633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Decompress the inflater's compressed data into the sink. 653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Segment tail = sink.writableSegment(1); 673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int bytesInflated = inflater.inflate(tail.data, tail.limit, Segment.SIZE - tail.limit); 683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bytesInflated > 0) { 693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller tail.limit += bytesInflated; 703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.size += bytesInflated; 713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return bytesInflated; 723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (inflater.finished() || inflater.needsDictionary()) { 743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller releaseInflatedBytes(); 753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return -1; 763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (sourceExhausted) throw new EOFException("source exhausted prematurely"); 783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (DataFormatException e) { 793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new IOException(e); 803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Refills the inflater with compressed data if it needs input. (And only if 863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * it needs input). Returns true if the inflater required input but the source 873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * was exhausted. 883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public boolean refill() throws IOException { 903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!inflater.needsInput()) return false; 913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller releaseInflatedBytes(); 933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (inflater.getRemaining() != 0) throw new IllegalStateException("?"); // TODO: possible? 943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // If there are compressed bytes in the source, assign them to the inflater. 963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (source.exhausted()) return true; 973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Assign buffer bytes to the inflater. 993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Segment head = source.buffer().head; 1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bufferBytesHeldByInflater = head.limit - head.pos; 1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller inflater.setInput(head.data, head.pos, bufferBytesHeldByInflater); 1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return false; 1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** When the inflater has processed compressed data, remove it from the buffer. */ 1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void releaseInflatedBytes() throws IOException { 1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bufferBytesHeldByInflater == 0) return; 1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller int toRelease = bufferBytesHeldByInflater - inflater.getRemaining(); 1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bufferBytesHeldByInflater -= toRelease; 1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller source.skip(toRelease); 1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public Source deadline(Deadline deadline) { 1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller source.deadline(deadline); 1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return this; 1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void close() throws IOException { 1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) return; 1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller inflater.end(); 1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closed = true; 1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller source.close(); 1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller} 125