com.adobe.internal.io.ByteArrayByteWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
*
* File: ByteArrayByteWriter.java
*
*
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 2005 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of
* Adobe Systems Incorporated and its suppliers, if any. The intellectual
* and technical concepts contained herein are proprietary to Adobe Systems
* Incorporated and its suppliers and may be covered by U.S. and Foreign
* Patents, patents in process, and are protected by trade secret or
* copyright law. Dissemination of this information or reproduction of this
* material is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
*/
package com.adobe.internal.io;
import java.io.IOException;
import java.util.ArrayList;
/**
* This class implements a ByteWriter in which the data is written into
* a byte array. The buffer automatically grows as data is written to it.
* The data can be retrieved by using toByteReader() and toByteArray().
*
* This class uses an array of byte buffers which are ascending powers
* of two in size. They are added automatically as they are needed. The array
* starts with TWO buffers of size mMinBufSize, then one each of buffers
* sized in ascending powers of two until mMaxBufSize is reached. There may
* then be an arbitrary number of buffers sized mMaxBufSize. Starting with
* TWO buffers of size mMinBufSize may seem odd, but it has the nice quality
* of making it easy to compute the buffer array index from ONLY the high
* order bit of the position address.
*
* This class is not threadsafe. It is not safe to pass an instance of this class
* to multiple threads. It is not safe to pass an instance of this class to multiple users even
* if in the same thread. It is not safe to give the same byte buffers to multiple instances
* of this class.
*/
public final class ByteArrayByteWriter implements ByteWriter
{
private byte[] mCurBuffer;
private ArrayList mBufferArray;
private int mCurIndex;
private int mCurBase;
private int mCurTop;
private int mLength;
private static final int mMinBufSize = 32; // Must be power of two
private int mMinBufBit;
private static final int mMaxBufSize = 262144; // A power of two larger than mMinBufSize
private int mMaxBufBit;
private int mTotalSize;
/**
* Create a new ByteArrayByteWriter with a default sized backing byte array.
*/
public ByteArrayByteWriter()
{
this(0);
}
/**
* Create a new ByteArrayByteReader with the given byte array.
* The buffer is not copied.
* @param buffer the byte array to use.
*/
public ByteArrayByteWriter(byte[] buffer)
{
this(buffer, 0, buffer.length);
}
/**
* Create a new ByteArrayByteWriter with a backing byte array of the size given.
* @param size the initial buffer size in bytes.
*/
public ByteArrayByteWriter(int size)
{
//System.out.println(" in con1");
if (size < 0)
{
throw new IllegalArgumentException("Negative initial size: " + size);
}
mCurBuffer = new byte[mMinBufSize];
mBufferArray = new ArrayList();
mBufferArray.add(mCurBuffer);
mCurIndex = 0;
mCurBase = 0;
mCurTop = mMinBufSize;
mLength = 0;
int bits, counter;
for (bits = mMinBufSize, counter = 0; bits != 0; counter++)
{
bits = bits >> 1;
}
mMinBufBit = counter - 1;
for (bits = mMaxBufSize, counter = 0; bits != 0; counter++)
{
bits = bits >> 1;
}
mMaxBufBit = counter - 1;
mTotalSize = mMinBufSize;
}
/**
* Create a new ByteArrayByteWriter with a backing byte array given.
* @param buffer
* @param offset
* @param length
*/
public ByteArrayByteWriter(byte[] buffer, int offset, int length)
{
//System.out.println(" in con2");
if ((offset < 0) || (length < 0) || ((offset + length) > buffer.length))
{
throw new IllegalArgumentException("Bad array parameters - offset = " + offset + ", length = " + length);
}
mCurBuffer = buffer;
mCurBase = -offset;
mCurTop = length;
mLength = length;
}
/**
* @param bufferArray
* @param length
*/
public ByteArrayByteWriter(ArrayList bufferArray, int length)
{
//System.out.println(" in readn con");
mCurIndex = 0;
mCurBuffer = (byte[])bufferArray.get(mCurIndex);
mBufferArray = bufferArray;
mCurBase = 0;
mCurTop = Math.min(length, mCurBuffer.length);
mLength = length;
//mMinBufSize = mCurBuffer.length;
int bits, counter;
for (bits = mMinBufSize, counter = 0; bits != 0; counter++)
{
bits = bits >> 1;
}
mMinBufBit = counter - 1;
//mMaxBufSize = ((byte[])(mBufferArray.get(mBufferArray.size() - 1))).length;
for (bits = mMaxBufSize, counter = 0; bits != 0; counter++)
{
bits = bits >> 1;
}
mMaxBufBit = counter - 1;
}
/**
* Copy out the backing store to a single byte array.
* This requires us to allocate a single byte array equal in size
* to the sum of all the component arrays. Clients are warned that
* if the total size is very large, they may get an OutOfMemory
* exception by doing this.
* @return the byte array copy.
*/
public byte[] toByteArray()
{
byte bufferCopy[] = new byte[mLength];
for (int index = 0, totalCount = 0; totalCount < mLength; index++)
{
byte[] thisBuffer = (byte[])mBufferArray.get(index);
int thisCount = Math.min(mLength - totalCount, thisBuffer.length);
System.arraycopy(thisBuffer, 0, bufferCopy, totalCount, thisCount);
totalCount += thisCount;
}
return bufferCopy;
}
/**
* @see com.adobe.internal.io.ByteWriter#write(long, int)
*/
public void write(long position, int b)
throws IOException
{
if (position < mCurBase || position >= mCurTop) {
if (position < 0 || position >= Integer.MAX_VALUE)
{
throw new IOException("Attempt to position outside the buffer.");
}
if (position >= mTotalSize)
{
resizeBuffer(position + 1);
}
reloadBuffer(position);
}
if (position >= mLength)
{
mLength = (int)position + 1;
}
mCurBuffer[(int)position - mCurBase] = (byte)b;
}
/**
* @see com.adobe.internal.io.ByteWriter#write(long, byte[], int, int)
*/
public void write(long position, byte[] b, int offset, int length)
throws IOException
{
if (position < 0 || position + length > Integer.MAX_VALUE)
{
throw new IOException("Attempt to position outside the buffer.");
}
if ((offset < 0) || (length < 0) || ((offset + length) > b.length))
{
throw new IOException("Invalid offset/length on the array.");
}
if (length == 0)
{
return;
}
if (position + length > mLength)
{
mLength = (int)position + length;
}
if (position >= mCurBase && position + length <= mCurTop)
{
System.arraycopy(b, offset, mCurBuffer, (int)position - mCurBase, length);
} else {
if (position + length > mTotalSize)
{
resizeBuffer(position + length);
}
if (position < mCurBase || position >= mCurTop)
{
reloadBuffer(position);
}
int totalWritten = 0;
while (totalWritten < length)
{
int thisWrite = Math.min(length - totalWritten, mCurTop - (int)position);
System.arraycopy(b, offset, mCurBuffer, (int)position - mCurBase, thisWrite);
totalWritten += thisWrite;
position += thisWrite;
offset += thisWrite;
if (totalWritten < length)
{
mCurIndex++;
mCurBase = mCurTop;
mCurBuffer = (byte[])mBufferArray.get(mCurIndex);
mCurTop = mCurBase + mCurBuffer.length;
}
}
}
}
/**
* Reload the buffer if needed. I decided to separate this out
* even though it costs the overhead of method invocation. It's
* performance sensitive. Maybe I should just break down and make
* it an ugly case statement, as Stuart suggests. For now, I try
* to check the obvious cases, like seeking to zero or moving to
* the next buffer in sequence, and then loop if that fails.
* @param position the new location to load the buffer for.
*/
private void reloadBuffer(long position)
{
if (position < mMinBufSize)
{
mCurIndex = 0;
mCurBase = 0;
}
else if (position == mCurTop)
{
mCurIndex++;
mCurBase = mCurTop;
}
else if (position >= mMaxBufSize)
{
mCurIndex = ((int)position / mMaxBufSize) + mMaxBufBit - mMinBufBit;
mCurBase = (int)position & ~(mMaxBufSize - 1);
} else {
int index = mMaxBufBit - mMinBufBit + 1;
int bits = (int)position << (31 - mMaxBufBit);
while (--index != 0)
{
if (((bits = bits << 1) & 0x80000000) != 0)
{
break;
}
}
mCurIndex = index;
mCurBase = (1 << (index + mMinBufBit - 1)) & ~(mMinBufSize - 1);
}
mCurBuffer = (byte[])mBufferArray.get(mCurIndex);
mCurTop = mCurBase + mCurBuffer.length;
}
/**
* Resize the buffer if needed and update the length information.
* @param newSize the new total size we require to complete this write.
* @throws IOException
*/
private void resizeBuffer(long newSize)
throws IOException
{
while (mTotalSize < newSize)
{
int bufferSize = 0;
int arraySize = mBufferArray.size();
if (arraySize < 2)
{
bufferSize = mMinBufSize;
} else {
bufferSize = ((byte[])mBufferArray.get(arraySize - 1)).length;
if (bufferSize < mMaxBufSize)
{
bufferSize = bufferSize * 2;
} else {
bufferSize = mMaxBufSize;
}
}
mBufferArray.add(new byte[bufferSize]);
mTotalSize += bufferSize;
}
}
/**
* @see com.adobe.internal.io.ByteWriter#length()
*/
public long length()
throws IOException
{
return mLength;
}
/**
* @see com.adobe.internal.io.ByteWriter#flush()
*/
public void flush()
throws IOException
{
}
/**
* @see com.adobe.internal.io.ByteWriter#close()
*/
public void close()
throws IOException
{
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
return super.toString();
}
/************* Reader methods**********************/
/**
* Create a new ByteArrayByteReader with the given buffer array.
* The data is not copied.
* @param bufferArray ArrayList of byte[] buffers to use.
* @param length the TOTAL length of all buffers in use.
*/
/**
* @see com.adobe.internal.io.ByteReader#read(long)
*/
public int read(long position)
throws IOException
{
if (position < 0 || position >= mLength)
return ByteReader.EOF;
if (position < mCurBase || position >= mCurTop) {
reloadBuffer(position);
}
return mCurBuffer[(int)position - mCurBase] & 0xff;
}
/**
* @see com.adobe.internal.io.ByteReader#read(long, byte[], int, int)
*/
public int read(long position, byte[] b, int offset, int length)
throws IOException
{
if (position < 0 || position >= mLength)
{
return ByteReader.EOF;
}
if ((offset < 0) || (length < 0) || ((offset + length) > b.length))
{
throw new IOException("Invalid offset/length on the array.");
}
if (length == 0)
{
return 0;
}
length = (int)Math.min(length, mLength - position);
if (position >= mCurBase && position + length <= mCurTop)
{
System.arraycopy(mCurBuffer, (int)position - mCurBase, b, offset, length);
} else {
if (position < mCurBase || position >= mCurTop)
{
reloadBuffer(position);
}
int totalRead = 0;
while (totalRead < length)
{
int thisRead = Math.min(length - totalRead, mCurTop - (int)position);
System.arraycopy(mCurBuffer, (int)position - mCurBase, b, offset, thisRead);
totalRead += thisRead;
position += thisRead;
offset += thisRead;
if (totalRead < length)
{
mCurIndex++;
mCurBase = mCurTop;
mCurBuffer = (byte[])mBufferArray.get(mCurIndex);
mCurTop = Math.min(mLength, mCurBase + mCurBuffer.length);
}
}
}
return length;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy