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

commonMain.com.hoc081098.flowext.bufferCount.kt Maven / Gradle / Ivy

/*
 * MIT License
 *
 * Copyright (c) 2021-2024 Petrus Nguyễn Thái Học
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.hoc081098.flowext

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

/**
 * This function is an alias to [bufferCount] operator.
 *
 * @see bufferCount
 */
public fun  Flow.chunked(bufferSize: Int): Flow> = bufferCount(bufferSize)

/**
 * Buffers the source [Flow] values until the size hits the maximum [bufferSize] given.
 *
 * Returns a [Flow] that emits buffers of items it collects from the current [Flow].
 * It emits connected, non-overlapping buffers, each containing [bufferSize] items.
 * When the current [Flow] completes, the resulting [Flow] emits the current buffer
 * and propagates the complete event from the current [Flow].
 * Note that if the current [Flow] throws an exception,
 * that exception is passed on immediately without first emitting the buffer it is in the process of assembling.
 *
 * @param bufferSize The maximum size of the buffer emitted.
 */
public fun  Flow.bufferCount(bufferSize: Int): Flow> = bufferExact(bufferSize)

/**
 * Buffers a number of values from the source [Flow] by [bufferSize]
 * then emits the buffer and clears it, and starts a new buffer each [startBufferEvery] values.
 *
 * When the current [Flow] completes, the resulting [Flow] emits active buffers
 * and propagates the complete event from the current [Flow].
 * Note that if the current [Flow] throws an exception,
 * that exception is passed on immediately without first emitting the buffer it is in the process of assembling.
 *
 * @param bufferSize The maximum size of the buffer emitted.
 * @param startBufferEvery Interval at which to start a new buffer.
 * For example if [startBufferEvery] is 2, then a new buffer will be started on every other value from the source.
 * A new buffer is started at the beginning of the source by default.
 */
public fun  Flow.bufferCount(bufferSize: Int, startBufferEvery: Int): Flow> {
  // If `startBufferEvery` is equals to `bufferSize`, then we're
  // opening and closing on the bufferSize itself.
  return if (startBufferEvery == bufferSize) {
    bufferExact(bufferSize)
  } else {
    bufferSkip(bufferSize, startBufferEvery)
  }
}

private fun  Flow.bufferSkip(bufferSize: Int, skip: Int): Flow> {
  return flow {
    val buffers = ArrayDeque>()
    var index = 0

    try {
      collect { element ->
        // Check to see if we need to start a buffer.
        // This will start one at the first value, and then
        // a new one every N after that.
        if (index++ % skip == 0) {
          buffers += mutableListOf()
        }

        // Push our value into our active buffers.
        buffers.iterator().let { iterator ->
          iterator.forEach { buffer ->
            buffer += element

            // Check to see if we're over the bufferSize
            // if we are, record it so we can emit it later.
            // If we emitted it now and removed it, it would
            // mutate the `buffers` array while we're looping
            // over it.
            if (buffer.size >= bufferSize) {
              // We have found some buffers that are over the
              // `bufferSize`. Emit them, and remove them from our
              // buffers list.
              iterator.remove()
              emit(buffer.toList())
            }
          }
        }
      }

      // When the source completes, emit all of our
      // active buffers.
      buffers.forEach { emit(it.toList()) }
    } finally {
      buffers.clear()
    }
  }
}

private fun  Flow.bufferExact(bufferSize: Int): Flow> {
  return flow {
    var buffer: MutableList? = null

    collect { element ->
      val b = buffer ?: mutableListOf().also { buffer = it }
      b += element

      if (b.size >= bufferSize) {
        emit(b)
        buffer = null
      }
    }

    // Emits remaining buffer
    buffer?.let { emit(it) }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy