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

net.sf.mmm.util.io.impl.AbstractByteArrayBufferBuffer Maven / Gradle / Ivy

/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.util.io.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.NoSuchElementException;

import net.sf.mmm.util.exception.api.NlsIllegalArgumentException;
import net.sf.mmm.util.exception.api.ValueOutOfRangeException;
import net.sf.mmm.util.io.api.BufferExceedException;
import net.sf.mmm.util.io.api.ByteArray;
import net.sf.mmm.util.io.api.ByteArrayBuffer;
import net.sf.mmm.util.io.api.ByteProcessor;
import net.sf.mmm.util.io.api.ComposedByteBuffer;
import net.sf.mmm.util.io.api.ProcessableByteArrayBuffer;
import net.sf.mmm.util.io.base.ByteArrayImpl;

/**
 * This is the abstract base implementation of the {@link ProcessableByteArrayBuffer} interface for a {@code byte[]}
 * -Buffer that represents the concatenation of multiple {@link ByteArrayBufferImpl}s. It has its own state
 * (read-pointer) and does NOT modify a contained {@link ByteArrayBufferImpl buffer} when reading. If one of the
 * underlying {@link ByteArrayBufferImpl buffers} has been read to the {@link ByteArrayBufferImpl#getMaximumIndex() end}
 * this class steps to the next one in a rotating way until the last buffer has been reached, that contains data that
 * has NOT been read before. Further this class allows to be {@link #fill(InputStream) (re)filled}. 
* NOTE:
* This class is NOT public visible, because further releases might break it's compatibility. Feel free to review and * give feedback on the mailing list if you want to use it directly. * * @author Joerg Hohwiller (hohwille at users.sourceforge.net) * @since 1.0.1 */ public abstract class AbstractByteArrayBufferBuffer implements ProcessableByteArrayBuffer, ComposedByteBuffer { /** The actual buffers. */ private final ByteArrayBufferImpl[] buffers; /** The index of the current buffer out of {@link #buffers}. */ private int buffersIndex; /** The index of the last buffer out of {@link #buffers}. */ private int buffersEndIndex; /** The number of buffers that have been stepped through. */ private int bufferStepCount; /** * The current position in {@link #buffers}[{@link #buffersIndex}]. */ private int currentBufferIndex; /** * The value of * {@link #buffers}[{@link #buffersIndex}].{@link ByteArrayBufferImpl#getMaximumIndex() getMaximumIndex()} * . */ private int currentBufferMax; /** * The value of {@link #buffers}[{@link #buffersIndex}].{@link ByteArrayBufferImpl#getBytes() getBytes()} * . */ private byte[] currentBufferBytes; /** * The constructor. * * @param buffers are the buffers to concat. */ public AbstractByteArrayBufferBuffer(ByteArrayBufferImpl... buffers) { super(); this.buffers = buffers; this.buffersIndex = 0; this.buffersEndIndex = 0; this.bufferStepCount = 0; ByteArrayBuffer buffer = this.buffers[this.buffersIndex]; this.currentBufferIndex = buffer.getCurrentIndex(); this.currentBufferMax = buffer.getMaximumIndex(); this.currentBufferBytes = buffer.getBytes(); while (buffer.hasNext()) { this.buffersEndIndex++; if (this.buffersEndIndex < this.buffers.length) { buffer = this.buffers[this.buffersIndex]; } else { this.buffersEndIndex--; break; } } } /** * The constructor used to copy from the given {@code template}. * * @param template is the buffer to copy. */ protected AbstractByteArrayBufferBuffer(AbstractByteArrayBufferBuffer template) { super(); this.buffers = template.buffers; this.buffersEndIndex = template.buffersEndIndex; this.buffersIndex = template.buffersIndex; this.bufferStepCount = template.bufferStepCount; this.currentBufferBytes = template.currentBufferBytes; this.currentBufferIndex = template.currentBufferIndex; this.currentBufferMax = template.currentBufferMax; } /** * This method gets the current {@link ByteArrayBufferImpl}. * * @return the current {@link ByteArrayBufferImpl}. */ protected ByteArrayBuffer getCurrentBuffer() { return this.buffers[this.buffersIndex]; } /** * This method gets the current index in the {@link #getCurrentBuffer() current buffer}. * * @return the position in the {@link #getCurrentBuffer() current buffer}. */ protected int getCurrentBufferIndex() { return this.currentBufferIndex; } /** * This method switches the {@link #getCurrentBuffer() current buffer} to the next available buffer. If this method is * called when the last buffer has already been reached, the {@link #getCurrentBufferIndex() index} will be set to * {@link #getCurrentBuffer()}.{@link ByteArrayBufferImpl#getMaximumIndex() getMaximumIndex()}+1 so the * end of this buffer is reached and {@link #hasNext()} will return {@code false}. * * @return {@code true} if there was a next buffer to switch to, {@code false} if the {@link #getCurrentBuffer() * current buffer} is already the last one. */ protected boolean nextBuffer() { if (this.buffersIndex == this.buffersEndIndex) { return false; } this.bufferStepCount++; this.buffersIndex++; if (this.buffersIndex >= this.buffers.length) { this.buffersIndex = 0; } ByteArrayBuffer buffer = this.buffers[this.buffersIndex]; this.currentBufferBytes = buffer.getBytes(); this.currentBufferIndex = buffer.getCurrentIndex(); this.currentBufferMax = buffer.getMaximumIndex(); return true; } @Override public int getBytesAvailable() { int count = this.currentBufferMax - this.currentBufferIndex + 1; int i = this.buffersIndex; while (i != this.buffersEndIndex) { i++; if (i >= this.buffers.length) { i = 0; } ByteArrayBuffer buffer = this.buffers[i]; count = count + buffer.getBytesAvailable(); } return count; } @Override public boolean hasNext() { if (this.currentBufferIndex <= this.currentBufferMax) { return true; } return nextBuffer(); } @Override public byte next() throws NoSuchElementException { if (this.currentBufferIndex <= this.currentBufferMax) { byte result = this.currentBufferBytes[this.currentBufferIndex++]; if (this.currentBufferIndex > this.currentBufferMax) { nextBuffer(); } return result; } else { throw new NoSuchElementException(); } } @Override public byte peek() throws NoSuchElementException { if (this.currentBufferIndex <= this.currentBufferMax) { return this.currentBufferBytes[this.currentBufferIndex]; } else { throw new NoSuchElementException(); } } @Override public long skip(long byteCount) { return process(null, byteCount); } @Override public long process(ByteProcessor processor, long byteCount) { if (byteCount < 0) { throw new NlsIllegalArgumentException(Long.valueOf(byteCount)); } long count = byteCount; while (count > 0) { int bytesLeft = this.currentBufferMax - this.currentBufferIndex + 1; int len; if (bytesLeft > count) { len = (int) count; } else { len = bytesLeft; } int consumed = len; if (processor != null) { consumed = processor.process(this.currentBufferBytes, this.currentBufferIndex, len); if ((consumed < 0) || (consumed > len)) { throw new ValueOutOfRangeException(Integer.valueOf(consumed), Integer.valueOf(0), Integer.valueOf(len), processor); } } this.currentBufferIndex = this.currentBufferIndex + consumed; count = count - consumed; if ((count == 0) || (consumed < len)) { break; } if (this.currentBufferIndex <= this.currentBufferMax) { break; } boolean hasNext = nextBuffer(); if (!hasNext) { break; } } return byteCount - count; } /** * This method synchronizes the buffer with the given {@code master}. * * @param master is the buffer this buffer was created from. */ protected void sync(AbstractByteArrayBufferBuffer master) { this.buffersEndIndex = master.buffersEndIndex; int bufferDist = master.bufferStepCount - this.bufferStepCount; if (bufferDist > 0) { this.buffersIndex = master.buffersIndex; this.bufferStepCount = master.bufferStepCount; ByteArrayBuffer buffer = this.buffers[this.buffersIndex]; this.currentBufferBytes = buffer.getBytes(); this.currentBufferIndex = buffer.getCurrentIndex(); this.currentBufferMax = buffer.getMaximumIndex(); } } /** * This method fills this buffer using the given {@code inputStream}. If the buffer is already filled, this method * will have no effect and return {@code false}. * * @param inputStream is the {@link InputStream} providing the data to fill this buffer with. * @throws IOException if caused by the {@code inputStream} whilst reading. * @return {@code true} if the end of the stream was encountered while (re)filling this buffer, {@code false} * otherwise. */ protected boolean fill(InputStream inputStream) throws IOException { int nextEnd = this.buffersEndIndex + 1; if (nextEnd >= this.buffers.length) { nextEnd = 0; } boolean todo = true; while (todo) { ByteArrayBufferImpl buffer = this.buffers[nextEnd]; if (nextEnd == this.buffersIndex) { if (buffer.hasNext()) { break; } else { // the buffer was initially completely empty... nextBuffer(); todo = false; } } if (nextEnd != this.buffersIndex) { int bytes = inputStream.read(buffer.getBytes()); if (bytes == 0) { // strange API but however give it another try... bytes = inputStream.read(buffer.getBytes()); } if (bytes > 0) { buffer.setCurrentIndex(0); buffer.setMaximumIndex(bytes - 1); } else { assert (bytes == -1); return true; } } this.buffersEndIndex = nextEnd; nextEnd++; if (nextEnd >= this.buffers.length) { nextEnd = 0; } } return false; } @Override public int fill(byte[] buffer, int offset, int length) { if (offset >= buffer.length) { throw new BufferExceedException(offset, buffer.length); } if ((length + offset) > buffer.length) { throw new BufferExceedException(length, buffer.length - offset); } if (!hasNext()) { // buffer is empty... return 0; } int bufferIndex = offset; int bytesLeft = length; while (bytesLeft > 0) { int count = this.currentBufferMax - this.currentBufferIndex + 1; if (count > bytesLeft) { count = bytesLeft; bytesLeft = 0; } else { bytesLeft = bytesLeft - count; } System.arraycopy(this.currentBufferBytes, this.currentBufferIndex, buffer, bufferIndex, count); bufferIndex = bufferIndex + count; this.currentBufferIndex = this.currentBufferIndex + count; if (this.currentBufferIndex > this.currentBufferMax) { boolean bufferLeft = nextBuffer(); if (!bufferLeft) { break; } } } return (length - bytesLeft); } @Override public ByteArray getByteArray(int index) { assert (index >= 0); assert (index < getByteArrayCount()); if (index == 0) { // TODO: use inner class as view instead of new object? return new ByteArrayImpl(this.currentBufferBytes, this.currentBufferIndex, this.currentBufferMax); } else { int newIndex = this.buffersIndex + index; if (newIndex > this.buffers.length) { if (this.buffersIndex <= this.buffersEndIndex) { newIndex = newIndex - this.buffers.length; } else { throw new NlsIllegalArgumentException(Integer.valueOf(index)); } } if (newIndex > this.buffersEndIndex) { throw new NlsIllegalArgumentException(Integer.valueOf(index)); } return this.buffers[newIndex]; } } @Override public int getByteArrayCount() { if (this.buffersIndex <= this.buffersEndIndex) { return this.buffersEndIndex - this.buffersIndex + 1; } else { return (this.buffers.length - this.buffersIndex) + this.buffersEndIndex; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy