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

de.unkrig.commons.io.pipe.AbstractRingBuffer Maven / Gradle / Ivy

There is a newer version: 1.2.4
Show newest version

/*
 * de.unkrig.commons - A general-purpose Java class library
 *
 * Copyright (c) 2014, Arno Unkrig
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *       following disclaimer in the documentation and/or other materials provided with the distribution.
 *    3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
 *       products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package de.unkrig.commons.io.pipe;

import java.io.IOException;

/**
 * Abstract implementation of a {@link Pipe} by a "backing store" of bytes with fixed size (the "capacity").
 * 

* Implementation must implement methods {@link #get(long, byte[], int, int)} and {@link #put(byte[], int, int, long)} * to provide access to the backing store. *

*/ public abstract class AbstractRingBuffer extends AbstractPipe { /** * @param capacity The size of the backing store */ public AbstractRingBuffer(long capacity) { this.capacity = capacity; } /** * Transfers len bytes from the backing store to buf{@code [off]...}. * * @param pos 0...capacity * @param off 0...buf{@code .length} * @param len 0...{@code min(capacity - pos, buf.length - off} * @see #AbstractRingBuffer(long) */ protected abstract void get(long pos, byte[] buf, int off, int len) throws IOException; /** * Transfers len bytes from {@code buf[off]...} to the backing store. * * @param off 0...{@code buf.length} * @param len 0...{@code min(capacity - pos, buf.length - off} * @param pos 0...capacity * @see #AbstractRingBuffer(long) */ protected abstract void put(byte[] buf, int off, int len, long pos) throws IOException; // ------------ END PUBLIC API -------------- /** * Size of the backing store; greater than zero. */ private final long capacity; /** * Index of the next byte to {@link #read(byte[], int, int)} from the backing store; {@code 0 ... capacity-1}; * {@code -1} means this ring buffer is empty. */ private long head = -1; /** * Index in the backing store where the next byte written will be stored; {@code 0 ... capacity-1}; undefined * iff this ring buffer is empty. */ private long tail; @Override public final int read(byte[] buf, int off, int len) throws IOException { if (len == 0) return 0; if (this.head == -1) return 0; // This ring buffer is empty. if (this.head == this.tail) { // This ring buffer is full. // Buffer structure: [ >= 0 allocated] [(tail = head) >= 1 allocated ] (capacity) long av = this.capacity - this.head; if (len < av) { this.get(this.head, buf, off, len); this.head += len; } else if (len >= this.capacity) { this.get(this.head, buf, off, (int) av); this.get(0, buf, off + (int) av, (int) (this.capacity - av)); this.head = -1; len = (int) this.capacity; } else { this.get(this.head, buf, off, (int) av); this.get(0, buf, off + (int) av, len - (int) av); this.head = len - av; } return len; } if (this.tail > this.head) { // Buffer structure: [ >= 0 free] [(head) >= 1 allocated ] [(tail) >= 1 free ] (capacity) long av = this.tail - this.head; if (len < av) { this.get(this.head, buf, off, len); this.head += len; return len; } else { this.get(this.head, buf, off, (int) av); this.head = -1; return (int) av; } } else { // Buffer structure: [ >= 0 allocated] [(tail) >= 0 free ] [(head) >= 1 allocated ] (capacity) long av = this.capacity - this.head; if (len < av) { this.get(this.head, buf, off, len); this.head += len; return len; } else if (len < av + this.tail) { this.get(this.head, buf, off, (int) av); this.get(0, buf, off + (int) av, len - (int) av); this.head = len - av; return len; } else { this.get(this.head, buf, off, (int) av); this.get(0, buf, off + (int) av, (int) this.tail); this.head = -1; return (int) (av + this.tail); } } } @Override public final int write(byte[] buf, int off, int len) throws IOException { if (len == 0) return 0; if (this.head == -1) { // This ring buffer is empty. if (len > this.capacity) len = (int) this.capacity; this.put(buf, off, len, 0); this.head = 0; this.tail = len; return len; } if (this.tail > this.head) { // Buffer structure: [ >= 0 free] [(head) >= 1 allocated ] [(tail) >= 1 free ] (capacity) long av = this.capacity - this.tail; if (len < av) { this.put(buf, off, len, this.tail); this.tail += len; return len; } else if (len == av) { this.put(buf, off, len, this.tail); this.tail = 0; return len; } else if (len > av + this.head) { this.put(buf, off, (int) av, this.tail); this.put(buf, off + (int) av, (int) this.head, 0); this.tail = this.head; return (int) (av + this.head); } else { this.put(buf, off, (int) av, this.tail); this.put(buf, off + (int) av, (int) (len - av), 0); this.tail = len - av; return len; } } else { // Buffer structure: [ >= 0 allocated] [(tail) >= 0 free ] [(head) >= 1 allocated ] (capacity) long av = this.head - this.tail; if (len > av) len = (int) av; this.put(buf, off, len, this.tail); this.tail += len; return len; } } @Override public final boolean isEmpty() { return this.head == -1; } @Override public final boolean isFull() { return this.head == this.tail; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy