/*
Copyright 2006,2007 Stefano Chizzolini. http://clown.stefanochizzolini.it
Contributors:
* Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it):
contributed code is Copyright 2006,2007 by Stefano Chizzolini.
This file should be part of the source code distribution of "PDF Clown library"
(the Program): see the accompanying README files for more info.
This Program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later version.
This Program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY, either expressed or implied; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
You should have received a copy of the GNU General Public License along with this
Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
Redistribution and use, with or without modification, are permitted provided that such
redistributions retain the above copyright notice, license and disclaimer, along with
this list of conditions.
*/
package it.stefanochizzolini.clown.bytes;
import it.stefanochizzolini.clown.bytes.filters.Filter;
import it.stefanochizzolini.clown.util.NotImplementedException;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Date;
/**
Byte buffer.
@version 0.0.4
*/
public final class Buffer
implements IBuffer
{
// <class>
// <static>
// <fields>
/**
Default buffer capacity.
*/
private static final int DefaultCapacity = 1 << 8;
private static final String DefaultCharset = "ISO-8859-1";
// </fields>
// </static>
// <dynamic>
// <fields>
/**
Inner buffer where data are stored.
*/
private byte[] data;
/**
Number of bytes actually used in the buffer.
*/
private int length;
/**
Pointer position within the buffer.
*/
private int position = 0;
private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
// </fields>
// <constructors>
public Buffer(
)
{this(0);}
public Buffer(
int capacity
)
{
if(capacity < 1)
capacity = DefaultCapacity;
this.data = new byte[capacity];
this.length = 0;
}
public Buffer(
byte[] data
)
{
this.data = data;
this.length = data.length;
}
public Buffer(
byte[] data,
ByteOrder byteOrder
)
{
this.data = data;
this.length = data.length;
this.byteOrder = byteOrder;
}
// </constructors>
// <interface>
// <public>
// <IBuffer>
public void append(
byte data
)
{
while(true)
{
try
{
this.data[this.length] = data;
break; // Escape the loop.
}
catch(Exception e)
{
// Do NOT additional data exceed buffer capacity?
if(!ensureCapacity(1)) // Unhandled exception.
{
// Propagate the exception!
throw new RuntimeException(e);
}
}
}
// Update buffer size!
this.length++;
}
public void append(
byte[] data
)
{
append(
data,
0,
data.length
);
}
public void append(
byte[] data,
int offset,
int length
)
{
while(true)
{
try
{
System.arraycopy(
data,
offset,
this.data,
this.length,
length
);
break; // Escape the loop.
}
catch(Exception e)
{
// Do NOT additional data exceed buffer capacity?
if(!ensureCapacity(length)) // Unhandled exception.
{
// Propagate the exception!
throw new RuntimeException(e);
}
}
}
// Update buffer size!
this.length += length;
}
public void append(
String data
)
{
try
{append(data.getBytes(DefaultCharset));}
catch(Exception e)
{throw new RuntimeException(e);}
}
public void append(
IInputStream data
)
{
append(
data.toByteArray(),
0,
(int)data.getLength()
);
}
public IBuffer clone(
)
{
IBuffer clone = new Buffer(getCapacity());
clone.append(data);
return clone;
}
public void decode(
Filter filter
)
{
data = filter.decode(data,0,length);
length = data.length;
}
public void delete(
int index,
int length
)
{
try
{
// Shift left the trailing data block to override the deleted data!
System.arraycopy(
this.data,
index + length,
this.data,
index,
this.length - (index + length)
);
}
catch(Exception e)
{throw new RuntimeException(e);}
// Update the buffer size!
this.length -= length;
}
public byte[] encode(
Filter filter
)
{return filter.encode(data,0,length);}
public int getByte(
int index
)
{return data[index];}
public byte[] getByteArray(
int index,
int length
)
{
byte[] data = new byte[length];
System.arraycopy(
this.data,
index,
data,
0,
length
);
return data;
}
public String getString(
int index,
int length
)
{
try
{return new String(data,index,length,DefaultCharset);}
catch(Exception e)
{throw new RuntimeException(e);}
}
public int getCapacity(
)
{return data.length;}
public void insert(
int index,
byte[] data
)
{
insert(
index,
data,
0,
data.length
);
}
public void insert(
int index,
byte[] data,
int offset,
int length
)
{
while(true)
{
try
{
// Shift right the existing data block to make room for new data!
System.arraycopy(
this.data,
index,
this.data,
index + length,
this.length - index
);
break; // Escape the loop.
}
catch(Exception e)
{
// Do NOT additional data exceed buffer capacity?
if(!ensureCapacity(length)) // Unhandled exception.
{
// Propagate the exception!
throw new RuntimeException(e);
}
}
}
// Insert additional data!
System.arraycopy(
data,
offset,
this.data,
index,
length
);
// Update the buffer size!
this.length += length;
}
public void insert(
int index,
String data
)
{
try
{
insert(
index,
data.getBytes(DefaultCharset)
);
}
catch(Exception e)
{
// Propagate the exception!
throw new RuntimeException(e);
}
}
public void insert(
int index,
IInputStream data
)
{
insert(
index,
data.toByteArray()
);
}
public void replace(
int index,
byte[] data
)
{
// Replace data!
System.arraycopy(
data,
0,
this.data,
index,
data.length
);
}
public void replace(
int index,
byte[] data,
int offset,
int length
)
{
// Replace data!
System.arraycopy(
data,
offset,
this.data,
index,
data.length
);
}
public void replace(
int index,
String data
)
{
try
{
// Replace data!
replace(
index,
data.getBytes(DefaultCharset)
);
}
catch(Exception e)
{throw new RuntimeException(e);}
}
public void replace(
int index,
IInputStream data
)
{
// Replace data!
replace(
index,
data.toByteArray()
);
}
public void setLength(
int value
)
{length = value;}
public void writeTo(
IOutputStream stream
)
{
stream.write(
data,
0,
length
);
}
// <IInputStream>
public ByteOrder getByteOrder(
)
{return byteOrder;}
public long getLength(
)
{return length;}
public long getPosition(
)
{return position;}
/* int hashCode() uses inherited implementation. */
public void read(
byte[] data
)
{
read(
data,
0,
data.length
);
}
public void read(
byte[] data,
int offset,
int length
)
{
try
{
System.arraycopy(
this.data,
position,
data,
offset,
length
);
position += length;
}
catch(Exception e)
{throw new RuntimeException(e);}
}
public byte readByte(
) throws EOFException
{
try
{return data[position++];}
catch(ArrayIndexOutOfBoundsException e)
{throw new EOFException();}
}
public int readInt(
) throws EOFException
{throw new NotImplementedException();}
public String readLine(
) throws EOFException
{
StringBuilder buffer = new StringBuilder();
try
{
while(true)
{
int c = data[position++];
if(c == '\r'
|| c == '\n')
break;
buffer.append((char)c);
}
}
catch(ArrayIndexOutOfBoundsException e)
{throw new EOFException();}
return buffer.toString();
}
public short readShort(
) throws EOFException
{throw new NotImplementedException();}
public String readString(
int length
)
{
try
{
String data = new String(
this.data,
position,
length,
DefaultCharset
);
position += length;
return data;
}
catch(Exception e)
{throw new RuntimeException(e);}
}
public int readUnsignedByte(
) throws EOFException
{
try
{return (data[position++] & 0xFF);}
catch(ArrayIndexOutOfBoundsException e)
{throw new EOFException();}
}
public int readUnsignedShort(
) throws EOFException
{
//TODO: harmonize byteorder semantics with C# version!!!
try
{
if(byteOrder == ByteOrder.LITTLE_ENDIAN)
return (data[position++] & 0xFF) | (data[position++] & 0xFF) << 8;
else // ByteOrder.BIG_ENDIAN
return (data[position++] & 0xFF) << 8 | (data[position++] & 0xFF);
}
catch(ArrayIndexOutOfBoundsException e)
{throw new EOFException();}
}
public void seek(
long position
)
{this.position = (int)position;}
public void setByteOrder(
ByteOrder value
)
{byteOrder = value;}
public void setPosition(
long value
)
{position = (int)value;}
public void skip(
long offset
)
{position += (int)offset;}
public byte[] toByteArray(
)
{
byte[] data = new byte[this.length];
System.arraycopy(
this.data,
0,
data,
0,
this.length
);
return data;
}
// </IInputStream>
// </IBuffer>
// </public>
// <protected>
/**
Check whether the buffer capacity has sufficient room for adding data.
*/
protected boolean ensureCapacity(
int additionalLength
)
{
int minCapacity = this.length + additionalLength;
// Is additional data within the buffer capacity?
if(minCapacity <= this.data.length)
return false; // OK -- No change.
// Additional data exceed buffer capacity.
// Reallocate the buffer!
byte[] data = new byte[
Math.max(
this.data.length << 1, // 1 order of magnitude greater than current capacity.
minCapacity // Minimum capacity required.
)
];
System.arraycopy(
this.data,
0,
data,
0,
this.length
);
this.data = data;
return true; // Reallocation happened.
}
}
|