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

com.day.io.ByteBuffer Maven / Gradle / Ivy

/*
 * Copyright 1997-2004 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.io;


/**
 * This class provides a byte buffer, that is unlimited in capacity.
 *
 * @version $Revision: 1.6 $, $Date: 2004-08-22 06:56:09 +0200 (Sun, 22 Aug 2004) $
 * @author ralph
 * @since antbear
 * @audience wad
 */
public class ByteBuffer {

    /** the total length of the valid data in the buffer */
    private int length = 0;

    /** the first chunk of the list */
    private Chunk firstChunk;

    /** the last chunk of the list */
    private Chunk lastChunk;

    /**
     * Creates a new ByteBuffer-object
     */
    public ByteBuffer() {
	reset();
    }

    /**
     * Resets this ByteBuffer and clears the internal storage.
     */
    public void reset() {
	firstChunk = lastChunk = new Chunk();
	length=0;
    }

    /**
     * Returns the entire buffer.
     * @return a new byte buffer.
     */
    public byte[] get() {
	byte[] ret = new byte[length];
	get(ret, 0, length);
	return ret;
    }

    /**
     * Returns up to num bytes from this ByteBuffer.
     *
     * @param num the number of bytes to return
     * @return a new buffer with the containig bytes
     */
    public byte[] get(int num) {
	byte[] ret = new byte[Math.min(num, length)];
	get(ret, 0, ret.length);
	return ret;
    }

    /**
     * Transferse up to num bytes to the output buffer
     * starting at offset.
     *
     * @param output the output buffer
     * @param offset the offset
     * @param length the number of bytes to transfer
     *
     * @return the number of bytes written
     */
    public int get(byte[] output, int offset, int num) {
	// limit num
	if (num > length) {
	    num = length;
	}
	int total = num;

	// transfer bytes
	while (num > 0) {
	    // transfer bytes from the chunk
	    int rd = firstChunk.get(output, offset, num);
	    offset+=rd;
	    num-=rd;
	    length-=rd;

	    if (firstChunk.isEmpty() && firstChunk.next!=null) {
		firstChunk = firstChunk.next;
	    }
	}
	return total;
    }


    /**
     * Puts length bytes from input starting at
     * offset to the end of this buffer.
     *
     * @param input the input buffer
     * @param offset the offset into the input buffer
     * @param length the number of bytes to transfer
     */
    public void put(byte[] input, int offset, int len) {
	if (len > 0) {
	    lastChunk = lastChunk.put(input, offset, len);
	    length += len;
	}
    }

    /**
     * Puts the bytes at the end of the buffer
     * @param bytes the bytes to add to the buffer
     */
    public void put(byte[] bytes) {
	if (bytes!=null) {
	    put(bytes, 0, bytes.length);
	}
    }

    /**
     * Puts one byte at the end of the buffer
     * to be optimized.
     */
    public void put(byte b) {
	lastChunk = lastChunk.put(b);
	length++;
    }

    /**
     * Returns the number of bytes in the queue.
     */
    public int length() {
	return length;
    }

    /**
     * Checks if the buffer is empty.
     * @return true if the buffer is empty;
     *         false otherwise.
     */
    public boolean isEmpty() {
	return length==0;
    }

    /**
     * Internal class that holds chunks of byte-arrays
     */
    private static class Chunk {

	/** the minumim size of a chunk */
	private static final int SIZE = 4096;

	/** the bytes in this array */
	private byte[] bytes;

	/** the offset to valid data */
	private int start;

	/** the length of valid data */
	private int end;

	/** the next chunk in the list */
	private Chunk next;

	/**
	 * Creates an empty Chunk.
	 */
	private Chunk() {
	    this(null, 0, 0);
	}

	/**
	 * Creates a Chunk containing one byte
	 */
	private Chunk(byte b) {
	    this(null, 0, 0);
	    bytes[start]=b;
	    end++;
	}

	/**
	 * Creates a new Chunk with the given byte array.
	 *
	 * @param input the byte array
	 */
	private Chunk(byte[] input) {
	    this(input, 0, input.length);
	}

	/**
	 * Creates a new Chunk out of a given byte array.
	 *
	 * @param input the input buffer
	 * @param offset the offset in the buffer
	 * @param len    the length in the buffer
	 */

	private Chunk(byte[] input, int offset, int len) {
	    bytes = new byte[Math.max(len, SIZE)];
	    start = 0;
	    end   = len;
	    if (len>0) {
		System.arraycopy(input, offset, bytes, 0, len);
	    }
	}

	/**
	 * Copies at most len bytes from the beginning of the internal
	 * storage to the output starting at offset.
	 * It also adapts the internal range of valid data.
	 *
	 * @param output the output buffer
	 * @param offset the offset in the output buffer
	 * @param len the number of bytes to transfer
	 *
	 * @return the number of transfered bytes.
	 */
	private int get(byte[] output, int offset, int len) {
	    if (len > length()) {
		len = length();
	    }
	    System.arraycopy(bytes, start, output, offset, len);
	    start += len;
	    if (start==end) {
		start=end=0;
	    }
	    return len;
	}

	/**
	 * Copies len bytes at the end of the internal storage.
	 * and creates new successors if neccesairy.
	 *
	 * @param input the input buffer
	 * @param offset the offset in the input buffer
	 * @param len the length of valid data in the input buffer
	 *
	 * @return the last chunk
	 */
	private Chunk put(byte[] input, int offset, int len) {
	    if (len < bytes.length - end) {
		System.arraycopy(input, offset, bytes, end, len);
		end+=len;
		return this;
	    } else {
		int num = bytes.length - end;
		System.arraycopy(input, offset, bytes, end, num);
		end = bytes.length;
		Chunk cnk = new Chunk(input, offset+num, len-num);
		cnk.next = this.next;
		this.next = cnk;
		return cnk;
	    }
	}

	/**
	 * Puts one byte at the end of the internal buffer. If neccessairy, it
	 * create a successor to store the byte.
	 *
	 * @param b the byte to append
	 * @return the last chunk
	 */
	private Chunk put(byte b) {
	    if (1 < bytes.length - end) {
		bytes[end++] = b;
		return this;
	    } else {
		Chunk cnk = new Chunk(b);
		cnk.next = this.next;
		this.next = cnk;
		return cnk;
	    }
	}


	/**
	 * Checks if this chunk is empty.
	 * @return true if this chunk is empty;
	 *         false otherwise.
	 */
	private boolean isEmpty() {
	    return start==end;
	}

	/**
	 * Returns the length of valid data in this chunk
	 * @return the length of this chunk
	 */
	private int length() {
	    return end-start;
	}
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy