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

io.deephaven.csv.densestorage.QueueReader Maven / Gradle / Ivy

package io.deephaven.csv.densestorage;

import io.deephaven.csv.containers.ByteSlice;
import io.deephaven.csv.util.MutableInt;

/** Companion to the {@link QueueWriter}. See the documentation there for details. */
public class QueueReader {
    /**
     * Sync object which synchronizes access to the "next" fields of every node in our linked list. Shared with the
     * QueueWriter.
     */
    private final Object sync;
    /** Current node. */
    private QueueNode node;
    /** Current block we are reading from, extracted from the current node. */
    protected TARRAY genericBlock;
    /**
     * Current offset in the current block. Updated as we read data. When the value reaches "end", then data in this
     * block is exhausted.
     */
    protected int current;
    /** "end" offset of the current block. */
    protected int end;

    /** Constructor. */
    protected QueueReader(Object sync, QueueNode node) {
        this.sync = sync;
        this.node = node;
        this.genericBlock = null;
        this.current = 0;
        this.end = 0;
    }

    /**
     * This method exists as a helper method for a subclass' tryGetXXX method. A typical implementation is in
     * CharReader:
     *
     * 
     * {@code
     * if (current + size > end) {
     *     if (!tryRefill(size)) {
     *         return false;
     *     }
     *     typedBlock = genericBlock;
     * }
     * }
     * 
* * The "if" in the caller is actually checking for multiple cases in a single comparison. One is a normal "buffer * empty, needs to be refilled" case. The other is a bad "something went terribly wrong" case. * *
    *
  • Case 1, The "buffer empty" case. Then {@code current == end}, and therefore {@code current + size > end} * (assuming {@code size > 0}, which it always is). Therefore, the 'if' inside the tryGetXXX code would evaluate to * true, so the tryGetXXX code would call this method. Then this method refills the buffer. *
  • Case 2: The buffer is not empty, but A logic error (which can't happen if the code is correct) has caused the * requested slice to go past the end of the block. Then {@code current < end} but {@code current + size > end}. * Again, the 'if' inside the tryGetXXX code would evaluate to true, so the tryGetXXX code would call this method. * But then the first line of our method detects the past-the-end condition and throws an exception. *
  • Case 3: The "buffer can satisfy the request" case. Then {@code current + size <= end}, so the 'if' inside the * tryGetXXX code would evaluate to false, and the tryGetXXX method doesn't call this method. *
*/ protected boolean tryRefill(int size) { if (current != end) { throw new RuntimeException("Logic error: slice straddled block"); } while (current == end) { if (node.isLast) { // Hygeine. node = null; genericBlock = null; current = 0; end = 0; return false; } synchronized (sync) { while (node.next == null) { catchyWait(sync); } node = node.next; genericBlock = node.data; current = node.begin; end = node.end; } } if (end - current < size) { throw new RuntimeException( String.format( "Logic error: got short block: expected at least %d, got %d", size, end - current)); } return true; } /** Call Object.wait() but suppress the need to deal with checked InterruptedExceptions. */ private static void catchyWait(Object o) { try { o.wait(); } catch (InterruptedException ie) { throw new RuntimeException( "Thread interrupted: probably cancelled in the CsvReader class due to some other exception."); } } /** A QueueReader specialized for bytes. */ public static final class ByteReader extends QueueReader { /** * Typed version of the current block. Saves us some implicit casting from the generic TARRAY object. This is a * performance optimization that may not matter. */ private byte[] typedBlock; /** Constructor. */ public ByteReader(final Object sync, final QueueNode head) { super(sync, head); } /** * Tries to get the next ByteSlice from the reader. * * @param size The exact number of chars to place in the slice. * @param bs The result, modified in place. * @return true If the next ByteSlice was successfully read; false if the end of input was reached. */ public boolean tryGetBytes(final int size, final ByteSlice bs) { if (current + size > end) { if (!tryRefill(size)) { return false; } typedBlock = genericBlock; } bs.reset(typedBlock, current, current + size); current += size; return true; } } /** A QueueReader specialized for ints. */ public static final class IntReader extends QueueReader { /** * Typed version of the current block. Saves us some implicit casting from the generic TARRAY object. This is a * performance optimization that may not matter. */ private int[] typedBlock; /** Constructor. */ public IntReader(Object sync, QueueNode head) { super(sync, head); } /** * Tries to get the next integer from the reader. * * @param result If the operation succeeds, contains the next integer. Otherwise, the contents are unspecified. * @return true if the next value was successfully read; false if the end of input was reached. */ public boolean tryGetInt(final MutableInt result) { if (current == end) { if (!tryRefill(1)) { return false; } typedBlock = genericBlock; } result.setValue(typedBlock[current++]); return true; } } /** A QueueReader specialized for byte arrays. */ public static final class ByteArrayReader extends QueueReader { /** * Typed version of the current block. Saves us some implicit casting from the generic TARRAY object. This is a * performance optimization that may not matter. */ private byte[][] typedBlock; public ByteArrayReader(final Object sync, final QueueNode head) { super(sync, head); } /** * Tries to get the next ByteSlice from the reader. * * @param bs The result, modified in place. * @return true If the next ByteSlice was successfully read; false if the end of input was reached. */ public boolean tryGetBytes(final ByteSlice bs) { if (current == end) { if (!tryRefill(1)) { return false; } typedBlock = genericBlock; } final byte[] data = typedBlock[current++]; bs.reset(data, 0, data.length); return true; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy