All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.okio.DeflaterSink.kt Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 * Copyright (C) 2014 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@file:JvmName("-DeflaterSinkExtensions")
@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.

package okio

import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
import java.io.IOException
import java.util.zip.Deflater

/**
 * A sink that uses [DEFLATE](http://tools.ietf.org/html/rfc1951) to
 * compress data written to another source.
 *
 * ### Sync flush
 *
 * Aggressive flushing of this stream may result in reduced compression. Each
 * call to [flush] immediately compresses all currently-buffered data;
 * this early compression may be less effective than compression performed
 * without flushing.
 *
 * This is equivalent to using [Deflater] with the sync flush option.
 * This class does not offer any partial flush mechanism. For best performance,
 * only call [flush] when application behavior requires it.
 */
class DeflaterSink
/**
 * This internal constructor shares a buffer with its trusted caller.
 * In general we can't share a BufferedSource because the deflater holds input
 * bytes until they are inflated.
 */
internal constructor(private val sink: BufferedSink, private val deflater: Deflater) : Sink {
  constructor(sink: Sink, deflater: Deflater) : this(sink.buffer(), deflater)

  private var closed = false

  @Throws(IOException::class)
  override fun write(source: Buffer, byteCount: Long) {
    checkOffsetAndCount(source.size, 0, byteCount)

    var remaining = byteCount
    while (remaining > 0) {
      // Share bytes from the head segment of 'source' with the deflater.
      val head = source.head!!
      val toDeflate = minOf(remaining, head.limit - head.pos).toInt()
      deflater.setInput(head.data, head.pos, toDeflate)

      // Deflate those bytes into sink.
      deflate(false)

      // Mark those bytes as read.
      source.size -= toDeflate
      head.pos += toDeflate
      if (head.pos == head.limit) {
        source.head = head.pop()
        SegmentPool.recycle(head)
      }

      remaining -= toDeflate
    }
  }

  @IgnoreJRERequirement
  private fun deflate(syncFlush: Boolean) {
    val buffer = sink.buffer
    while (true) {
      val s = buffer.writableSegment(1)

      // The 4-parameter overload of deflate() doesn't exist in the RI until
      // Java 1.7, and is public (although with @hide) on Android since 2.3.
      // The @hide tag means that this code won't compile against the Android
      // 2.3 SDK, but it will run fine there.
      val deflated = if (syncFlush) {
        deflater.deflate(s.data, s.limit, Segment.SIZE - s.limit, Deflater.SYNC_FLUSH)
      } else {
        deflater.deflate(s.data, s.limit, Segment.SIZE - s.limit)
      }

      if (deflated > 0) {
        s.limit += deflated
        buffer.size += deflated
        sink.emitCompleteSegments()
      } else if (deflater.needsInput()) {
        if (s.pos == s.limit) {
          // We allocated a tail segment, but didn't end up needing it. Recycle!
          buffer.head = s.pop()
          SegmentPool.recycle(s)
        }
        return
      }
    }
  }

  @Throws(IOException::class)
  override fun flush() {
    deflate(true)
    sink.flush()
  }

  internal fun finishDeflate() {
    deflater.finish()
    deflate(false)
  }

  @Throws(IOException::class)
  override fun close() {
    if (closed) return

    // Emit deflated data to the underlying sink. If this fails, we still need
    // to close the deflater and the sink; otherwise we risk leaking resources.
    var thrown: Throwable? = null
    try {
      finishDeflate()
    } catch (e: Throwable) {
      thrown = e
    }

    try {
      deflater.end()
    } catch (e: Throwable) {
      if (thrown == null) thrown = e
    }

    try {
      sink.close()
    } catch (e: Throwable) {
      if (thrown == null) thrown = e
    }

    closed = true

    if (thrown != null) throw thrown
  }

  override fun timeout(): Timeout = sink.timeout()

  override fun toString() = "DeflaterSink($sink)"
}

/**
 * Returns an [DeflaterSink] that DEFLATE-compresses data to this [Sink] while writing.
 *
 * @see DeflaterSink
 */
inline fun Sink.deflate(deflater: Deflater = Deflater()): DeflaterSink =
  DeflaterSink(this, deflater)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy