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

com.adobe.internal.io.ByteArrayByteWriter Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 *
 *	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