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

org.jooq.lambda.SeqBuffer Maven / Gradle / Ivy

Go to download

jOOλ is part of the jOOQ series (along with jOOQ, jOOX, jOOR, jOOU) providing some useful extensions to Java 8 lambdas.

There is a newer version: 0.9.15
Show newest version
package org.jooq.lambda;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
 * Lazily consumes given Spliterator through Seqs provided by method seq().
 * This method may be called multiple times, and the returned Seqs may be consumed interchangeably.
 *
 * Instances of this class ARE thread-safe.
 *
 * @author Tomasz Linkowski
 */
final class SeqBuffer {

    @SuppressWarnings("unchecked")
    static  SeqBuffer of(Stream stream) {
        return of((Spliterator) stream.spliterator());
    }

    static  SeqBuffer of(Spliterator spliterator) {
        if (spliterator instanceof SeqBuffer.BufferSpliterator) {
            return ((SeqBuffer.BufferSpliterator) spliterator).parentSeqBuffer(); // reuse existing SeqBuffer
        }
        return new SeqBuffer<>(spliterator);
    }

    private final Spliterator source;
    private final List buffer = new ArrayList<>();

    /**
     * True while source hasn't reported that it's exhausted.
     *
     * This volatile field acts as a memory barrier for the contents of source and buffer:
     *
     * @link http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
     */
    private volatile boolean buffering = true;

    private SeqBuffer(Spliterator source) {
        this.source = Objects.requireNonNull(source);
    }

    /**
     * Returns a Seq over given source.
     *
     * Although such Seq is not thread-safe by itself, it interacts in a thread-safe way with its
     * parent SeqBuffer. As a result, each Seq returned by this method can be safely
     * used on a different thread.
     */
    Seq seq() {
        return Seq.seq(new BufferSpliterator());
    }

    /**
     * Special Spliterator whose tryAdvance method can buffer
     * (i.e. can advance the source spliterator).
     *
     * Instances of this class are NOT thread-safe but they interact in a thread-safe way with SeqBuffer.
     */
    private class BufferSpliterator implements Spliterator {

        /**
         * Index of the element that will be returned upon next call to tryAdvance if such element exists.
         */
        private int nextIndex = 0;

        //
        // TRY ADVANCE
        //
        @Override
        public boolean tryAdvance(Consumer action) {
            return buffering // volatile-read (ensures buffer is up-to-date)
                  ? tryAdvanceThisWithBuffering(action) // slow (synchronized)
                  : tryAdvanceThisAtOnce(action); // fast (not synchronized)
        }

        /**
         * Tries to advance this Spliterator to element at nextIndex,
         * buffering source elements into buffer if necessary.
         *
         * Synchronized on buffer in order to:
         * - obtain accurate buffer.size()
         * - safely copy from source to buffer (if needed)
         * - safely call buffer.get()
         */
        private boolean tryAdvanceThisWithBuffering(Consumer action) {
            final T next;
            synchronized (buffer) {
                if (!canAdvanceThisWithBuffering())
                    return false;

                next = advanceThis();
            }

            action.accept(next); // call "action" outside of synchronized block
            return true;
        }

        private boolean canAdvanceThisWithBuffering() {
            return canAdvanceThisAtOnce() || tryAdvanceSource() && canAdvanceThisAtOnce();
        }

        private boolean canAdvanceThisAtOnce() {
            return nextIndex < buffer.size();
        }

        /**
         * Buffers (i.e. advances the source) until an item at nextIndex
         * is added to the buffer, or until the source is exhausted.
         *
         * Guarded by: buffer
         */
        private boolean tryAdvanceSource() {
            boolean canAdvanceSource = buffering; // volatile-read (stored in local variable to limit R/W operations)
            if (!canAdvanceSource) // check again after having synchronized
                return false;

            do {
                canAdvanceSource = source.tryAdvance(buffer::add);
            } while (canAdvanceSource && !canAdvanceThisAtOnce());

            // volatile-write (causes grown buffer and shrunk source to be visible to all threads upon next volatile-read)
            buffering = canAdvanceSource;
            return true;
        }

        private T advanceThis() {
            return buffer.get(nextIndex++);
        }

        /**
         * Called only when buffering has been completed.
         */
        private boolean tryAdvanceThisAtOnce(Consumer action) {
            if (!canAdvanceThisAtOnce())
                return false;

            action.accept(advanceThis());
            return true;
        }

        //
        // ESTIMATE SIZE
        //
        @Override
        public long estimateSize() {
            return buffering // volatile-read (ensures buffer is up-to-date)
                  ? estimateSizeDuringBuffering() // slow (synchronized)
                  : numberOfElementsLeftInBuffer(); // fast (not synchronized)
        }

        /**
         * Returns the estimate size of this Spliterator.
         *
         * Synchronized to get an accurate sum of buffer.size() and source.estimateSize().
         */
        private long estimateSizeDuringBuffering() {
            synchronized (buffer) {
                int leftInBuffer = numberOfElementsLeftInBuffer();
                if (!buffering) // check again after having synchronized
                    return leftInBuffer;

                long estimateSize = leftInBuffer + source.estimateSize();
                // will overflow to negative number if source.estimateSize() reports Long.MAX_VALUE
                return estimateSize >= 0 ? estimateSize : Long.MAX_VALUE;
            }
        }

        private int numberOfElementsLeftInBuffer() {
            return buffer.size() - nextIndex;
        }

        //
        // REMAINING
        //
        @Override
        public Spliterator trySplit() {
            return null;
        }

        @Override
        public int characteristics() {
            // no synchronization here because source.characteristics() is assumed to be thread-safe
            return (source.characteristics() & ~Spliterator.CONCURRENT) | Spliterator.ORDERED
                  | (buffering ? 0 : Spliterator.SIZED);
        }

        @Override
        public Comparator getComparator() {
            return source.getComparator();
        }

        SeqBuffer parentSeqBuffer() {
            return SeqBuffer.this;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy