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

commonMain.org.jetbrains.letsPlot.util.pngj.BufferedStreamFeeder.kt Maven / Gradle / Ivy

There is a newer version: 4.8.0
Show newest version
/*
 * Copyright (c) 2023 JetBrains s.r.o.
 * Use of this source code is governed by the MIT license that can be found in the LICENSE file.
 *
 * This file has been modified by JetBrains : Java code has been converted to Kotlin code.
 *
 * THE FOLLOWING IS THE COPYRIGHT OF THE ORIGINAL DOCUMENT:
 *
 * Copyright (c) 2009-2012, Hernán J. González.
 * Licensed under the Apache License, Version 2.0.
 *
 * The original PNGJ library is written in Java and can be found here: [PNGJ](https://github.com/leonbloy/pngj).
 */

package org.jetbrains.letsPlot.util.pngj

import kotlin.jvm.JvmOverloads

/**
 * Reads bytes from an input stream, and feeds a IBytesConsumer.
 */
internal class BufferedStreamFeeder @JvmOverloads constructor(
    private val stream: InputPngStream,
    bufSize: Int = DEFAULTSIZE
) {
    private val buf = ByteArray(if (bufSize < 1) DEFAULTSIZE else bufSize)
    private var pendinglen // bytes read+stored in buf, not yet still sent to IBytesConsumer
            = 0
    private var offset = 0

    /**
     * @return EOF on stream, or close() was called
     */
    private var isEof = false // EOF on inputStream
    private var closeStream = true
    private var bytesRead: Long = 0

    /**
     * @see BufferedStreamFeeder.feed
     */
    fun feed(consumer: IBytesConsumer): Int {
        return feed(consumer, Int.MAX_VALUE)
    }

    /**
     * Tries to feed the consumer with bytes read from the stream, at most
     * maxbytes
     *
     *
     * It can return less than maxbytes (that doesn't mean that the consumer or
     * the input stream is done)
     *
     *
     * Returns 0 if premature ending (no more to read, consumer not done) 

* Returns -1 if nothing fed, but consumer is done */ private fun feed(consumer: IBytesConsumer, maxBytes: Int): Int { refillBufferIfAppropiate() val consumed: Int val toFeed = if (maxBytes in 1 until pendinglen) maxBytes else pendinglen if (toFeed > 0) { consumed = consumer.consume(buf, offset, toFeed) // never returns 0 if (consumed > 0) { offset += consumed pendinglen -= consumed require(pendinglen >= 0) } } else { // nothing to fed ? premature ending ? if (!isEof) throw PngjInputException("This should not happen") return if (consumer.isDone) -1 else 0 /* premature ending */ } return if (consumed > 0) { consumed } else { // read bytes, but consumer refused to eat them ? (rare) if (!consumer.isDone) throw PngjInputException("This should not happen!") -1 } } /** * Feeds as much bytes as it can to the consumer, in a loop.

* Returns bytes actually consumed

* This will stop when either the input stream is eof, or when the consumer * refuses to eat more bytes. The caller can distinguish both cases by * calling [.hasPendingBytes] */ fun feedAll(consumer: IBytesConsumer): Long { var n: Long = 0 while (hasPendingBytes()) { val n1 = feed(consumer) if (n1 <= 0) break n += n1.toLong() } return n } /** * Feeds exactly nbytes, retrying if necessary * * @param consumer * Consumer * @param nbytes * Number of bytes * @return nbytes if success, 0 if premature input ending, -1 if consumer * done */ fun feedFixed(consumer: IBytesConsumer, nbytes: Int): Int { var remain = nbytes while (remain > 0) { val n = feed(consumer, remain) if (n <= 0) return n remain -= n } require(remain == 0) return nbytes } /** * If there are not pending bytes to be consumed, tries to fill the buffer * reading bytes from the stream. * * If EOF is reached, sets eof=TRUE and calls close() * * Find in pendinglen the amounts of bytes read. * * If IOException, throws a PngjInputException */ private fun refillBufferIfAppropiate() { if (pendinglen > 0 || isEof) return // only if not pending data // try to read offset = 0 pendinglen = stream.read(buf) if (pendinglen == 0) throw PngjInputException("This should not happen: stream.read(buf) returned 0") else if (pendinglen < 0) close() // this sets EOF and pendinglen=0 else bytesRead += pendinglen.toLong() // on return, either pendinglen > 0 or eof == true } /** * Returuns true if we have more data to fed the consumer. This might try to * grab more bytes from the stream if necessary */ fun hasPendingBytes(): Boolean { refillBufferIfAppropiate() return pendinglen > 0 } /** * @param closeStream * If true, the underlying stream will be closed on when close() * is called */ fun setCloseStream(closeStream: Boolean) { this.closeStream = closeStream } /** * Closes this object. * * Sets EOF=true, and closes the stream if closeStream is true * * This can be called internally, or from outside. * * Idempotent, secure, never throws exception. */ fun close() { isEof = true pendinglen = 0 offset = 0 if (closeStream) { stream.close() } } companion object { private const val DEFAULTSIZE = 16384 } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy