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

com.pdftools.sys.MemoryStream Maven / Gradle / Ivy

Go to download

The Pdftools SDK is a comprehensive development library that lets developers integrate advanced PDF functionalities into in-house applications.

The newest version!
package com.pdftools.sys;

import java.util.LinkedList;
import java.util.ListIterator;

/**
 * The stream implementation for in-memory processing.
 *
 * This can be used to read from or write to byte arrays.
 */
public class MemoryStream implements Stream
{
    private static final long MAX_MEMORY_SIZE = 2147483648L; // 2GB
    private static final int DEFAULT_BLOCK_SIZE = 8192;

    /**
     * Create a new memory stream.
     * @param initialCapacity The initial capacity of the stream. The length of the stream is still 0
     * @param blockSize The size of the memory blocks used to store the data
     */
    private MemoryStream(long initialCapacity, int blockSize)
    {
        this.blockSize = blockSize;
        this.list = new LinkedList();
        this.setMinCapacity(initialCapacity);
    }

    /**
     * Create a new memory stream.
     * @param initialCapacity The initial capacity of the stream. The length of the stream is still 0.
     */
    private MemoryStream(long initialCapacity)
    {
        this(initialCapacity, DEFAULT_BLOCK_SIZE);
    }

    /**
     * Create a new memory stream with initial capacity of 0.
     */
    public MemoryStream()
    {
        this(0);
    }

    /**
     * Create a new memory stream by copying from a buffer.
     * @param buffer The buffer from which the initial data is copied
     * @param offset The offset where the first byte from the buffer is copied
     * @param length The number of bytes that are copied from the buffer
     * @param blockSize The size of the memory blocks used to store the data
     */
    private MemoryStream(byte[] buffer, int offset, int length, int blockSize)
    {
        this(length, blockSize);
        this.write(buffer, offset, length);
    }

    /**
     * Create a new memory stream by copying from a buffer.
     * @param buffer The buffer from which the initial data is copied
     * @param offset The offset where the first byte from the buffer is copied
     * @param length The number of bytes that are copied from the buffer
     */
    public MemoryStream(byte[] buffer, int offset, int length)
    {
        this(buffer, offset, length, DEFAULT_BLOCK_SIZE);
    }

    /**
     * Create a new memory stream by copying from a buffer.
     * @param buffer The buffer from which the initial data is copied
     */
    public MemoryStream(byte[] buffer)
    {
        this(buffer, 0, buffer.length);
    }

    /**
     * Create a new memory stream by copying a stream.
     * @param stream The stream from which the initial data is copied
     * @param blockSize The size of the memory blocks used to store the data
     */
    private MemoryStream(Stream stream, int blockSize) throws java.io.IOException
    {
        this(stream.getLength(), blockSize);
        byte[] buffer = new byte[blockSize];
        int read;
        while ((read = stream.read(buffer, 0, blockSize)) > 0)
            write(buffer, 0, read);
    }

    /**
     * Create a new memory stream by copying a stream.
     * @param stream The stream from which the initial data is copied
     * @throws java.io.IOException if an I/O error occurs
     */
    public MemoryStream(Stream stream) throws java.io.IOException
    {
        this(stream, DEFAULT_BLOCK_SIZE);
    }

     /**
     * Create a new memory stream by copying the given stream.
     * @param inStream The stream from which the initial data is copied
     * @throws java.io.IOException if an I/O error occurs
     */
    public MemoryStream(java.io.InputStream inStream) throws java.io.IOException
    {
    	initialize(inStream, DEFAULT_BLOCK_SIZE);
    }

    private void initialize(java.io.InputStream inStream, int blockSize) throws java.io.IOException
    {
        this.blockSize = blockSize;
        this.list = new LinkedList();
        byte[] buffer = new byte[blockSize];
        int read;
        while ((read = inStream.read(buffer)) > 0)
        	write(buffer, 0, read);
    }

    /**
     * Get the length of the stream in bytes.
     * @return the length of the stream  in bytes
     */
    public long getLength()
    {
        return this.length;
    }

    /**
     * Set byte position.
     * @param position The new position of the stream (-1 for EOS)
     * @return true if successful
     */
    public boolean seek(long position)
    {
        this.position = position;
        return true;
    }

    /**
     * Get current byte position.
     * @return byte position, -1 if position unknown
     */
    public long tell()
    {
        return this.position;
    }

    /**
     * Read from the stream.
     * @param buffer The buffer where the data is written
     * @param offset The starting element in the buffer
     * @param length The maximum number of bytes to be read
     * @return The actual number of bytes read (-1 if EOS)
     */
    public int read(byte[] buffer, int offset, int length)
    {
        if (buffer == null)
            throw new NullPointerException("'buffer'");

        if (offset < 0 || offset + length > buffer.length)
            throw new IndexOutOfBoundsException();

        // EOS
        if (this.position == this.length)
            return -1;

        ListIterator it = this.list.listIterator((int)(this.position / this.blockSize));
        int currPos = offset;
        while (length > 0 && it.hasNext() && this.position < this.length)
        {
            int blockPos = (int)(this.position % this.blockSize);
            int currLength = Math.min(length, Math.min(this.blockSize - blockPos, (int)(this.length - this.position)));
            System.arraycopy(it.next(), blockPos, buffer, currPos, currLength);
            currPos += currLength;
            this.position += currLength;
            length -= currLength;
        }
        int read = currPos - offset;
        return read;
    }

    /**
     * Read from the stream.
     * @param buffer The buffer where the data is written to
     * @return The actual number of bytes read (-1 if EOS)
     */
	public int read(byte[] buffer) {
		return read(buffer, 0, buffer.length);
	}

    /**
     * Creates a newly allocated byte array. Its size is the current size of this stream and the contents have been copied into it.
     * @return The current contents of this stream as a byte array.
     * @throws OutOfMemoryError if an array larger than 2GB would be required to store the bytes.
     */
    public byte[] toByteArray()
    {
		if (this.length > MAX_MEMORY_SIZE)
			throw new OutOfMemoryError("Cannot allocate more than 2GB of memory.");

		byte[] buffer = new byte[(int)(this.length)];

		ListIterator it = this.list.listIterator(0);
		int currPos = 0;
		while (it.hasNext()) {
			int currLength = Math.min(this.blockSize, (int) (this.length - currPos));
			System.arraycopy(it.next(), 0, buffer, currPos, currLength);
			currPos += currLength;
		}
		return buffer;
    }

	/**
	 * Read data from the input stream and write it to this stream.
     * The data is appended at the stream's current byte position.
	 * @param inStream The stream from which the data is copied
	 * @return The actual number of transferred bytes
	 * @throws java.io.IOException if an I/O error occurs
	 */
	public int transferFrom(java.io.InputStream inStream) throws java.io.IOException
	{
        if (inStream == null)
            throw new NullPointerException("'inStream'");

		byte[] buffer = new byte[blockSize];
		int read;
		int total = 0;
		while ((read = inStream.read(buffer)) > 0)
		{
			write(buffer, 0, read);
			total += read;
		}
		return total;
	}

	/**
	 * Read data from this stream and write it to the output stream.
     * The data is read starting from the stream's current byte position.
     * In order to read the entire stream, make sure to first position
     * the stream at the beginning using {@code seek(0)}.
	 * @param outStream The stream to which the data is copied
	 * @return The actual number of transfered bytes
	 * @throws java.io.IOException if an I/O error occurs
	 */
	public long transferTo(java.io.OutputStream outStream) throws java.io.IOException 
	{
        if (outStream == null)
            throw new NullPointerException("'outStream'");

		byte[] buffer = new byte[blockSize];
		int read;
		long total = 0;
		while ((read = read(buffer)) > 0) 
		{
			outStream.write(buffer, 0, read);
			total += read;
		}
		return total;
	}

    /**
     * Write to the stream.
     * @param buffer The buffer where the data lies
     * @param offset The starting element in the buffer
     * @param length The maximum number of bytes to be written
     */
    public void write(byte[] buffer, int offset, int length)
    {
        if (buffer == null)
            throw new NullPointerException("'buffer'");

        if (offset < 0 || offset + length > buffer.length)
            throw new IndexOutOfBoundsException();

        this.setMinLength(this.position + length);

        ListIterator it = this.list.listIterator((int)(this.position / this.blockSize));
        int currPos = offset;
        while (length > 0 && it.hasNext())
        {
            int blockPos = (int)(this.position % this.blockSize);
            int currLength = Math.min(length, this.blockSize - blockPos);
            System.arraycopy(buffer, currPos, it.next(), blockPos, currLength);
            currPos += currLength;
            this.position += currLength;
            length -= currLength;
        }
    }

    public void close() throws java.io.IOException {}

    private boolean setMinLength(long number)
    {
        if (number < 0)
            return false;

        if (!this.setMinCapacity(number))
            return false;

        if (this.length < number)
            this.length = number;

        return true;
    }

    private boolean setMinCapacity(long number)
    {
        if (number < 0)
            return false;

        return this.setMinBlockNumber((int)((number + this.blockSize - 1) / this.blockSize));
    }

    private boolean setMinBlockNumber(int number)
    {
        if (number < 0)
            return false;

        for (int change = number - this.list.size(); change > 0; change--)
            this.list.addLast(new byte[this.blockSize]);

        return true;
    }

    private LinkedList list = null;
    private long length = 0;
    private long position = 0;
    int blockSize;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy