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