/*
Copyright 2006,2008 Stefano Chizzolini. http://clown.stefanochizzolini.it
Contributors:
* Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it)
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.
*/
using it.stefanochizzolini.clown.bytes.filters;
using System;
using System.IO;
using System.Text;
namespace it.stefanochizzolini.clown.bytes{
//TODO:IMPL Substitute System.Array static class invocations with System.Buffer static class invocations (better performance)!!!
/**
<summary>Byte buffer.</summary>
*/
public sealed class Buffer
: IBuffer,
IOutputStream
{
#region static
#region fields
/**
<summary>Default buffer capacity.</summary>
*/
private const int DefaultCapacity = 1 << 8;
private static readonly Encoding Encoding = Encoding.GetEncoding("ISO-8859-1");
#endregion
#endregion
#region dynamic
#region fields
/**
<summary>Inner buffer where data are stored.</summary>
*/
private byte[] data;
/**
<summary>Number of bytes actually used in the buffer.</summary>
*/
private int length;
/**
<summary>Pointer position within the buffer.</summary>
*/
private int position = 0;
#endregion
#region 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(
System.IO.Stream data
) : this((int)data.Length)
{Append(data);}
#endregion
#region interface
#region public
#region IBuffer
public void Append(
byte data
)
{
InsertData: try
{
this.data[this.length] = data;
}
catch
{
// Do additional data exceed buffer capacity?
if(EnsureCapacity(1))
{
// Retry!
goto InsertData;
}
else // Unhandled exception.
{
// Propagate the exception!
throw;
}
}
// Update buffer size!
this.length++;
}
public void Append(
byte[] data
)
{
Append(
data,
0,
data.Length
);
}
public void Append(
byte[] data,
int offset,
int length
)
{
InsertData: try
{
Array.Copy(
data,
offset,
this.data,
this.length,
length
);
}
catch
{
// Do additional data exceed buffer capacity?
if(EnsureCapacity(length))
{
// Retry!
goto InsertData;
}
else // Unhandled exception.
{
// Propagate the exception!
throw;
}
}
// Update the buffer size!
this.length += length;
}
public void Append(
string data
)
{Append(Encoding.GetBytes(data));}
public void Append(
IInputStream data
)
{
Append(
data.ToByteArray(),
0,
(int)data.Length
);
}
public void Append(
System.IO.Stream data
)
{
byte[] array = new byte[data.Length];
data.Position = 0; // Force reading from BOF.
data.Read(
array,
0,
array.Length
);
Append(array);
}
public int Capacity
{get{return data.Length;}}
public IBuffer Clone(
)
{
IBuffer clone = new Buffer(Capacity);
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!
Array.Copy(
this.data,
index + length,
this.data,
index,
this.length - (index + length)
);
}
catch
{throw;}
// 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];
Array.Copy(
this.data,
index,
data,
0,
length
);
return data;
}
public string GetString(
int index,
int length
)
{return Encoding.GetString(data,index,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
)
{
InsertData: try
{
// Shift right the existing data block to make room for new data!
Array.Copy(
this.data,
index,
this.data,
index + length,
this.length - index
);
}
catch
{
// Do additional data exceed buffer capacity?
if(EnsureCapacity(length))
{
// Retry!
goto InsertData;
}
else // Unhandled exception.
{
// Propagate the exception!
throw;
}
}
// Insert additional data!
Array.Copy(
data,
offset,
this.data,
index,
length
);
// Update the buffer size!
this.length += length;
}
public void Insert(
int index,
string data
)
{
Insert(
index,
Encoding.GetBytes(data)
);
}
public void Insert(
int index,
IInputStream data
)
{
Insert(
index,
data.ToByteArray()
);
}
public void Replace(
int index,
byte[] data
)
{
// Replace data!
Array.Copy(
data,
0,
this.data,
index,
data.Length
);
}
public void Replace(
int index,
byte[] data,
int offset,
int length
)
{
// Replace data!
Array.Copy(
data,
offset,
this.data,
index,
data.Length
);
}
public void Replace(
int index,
string data
)
{
// Replace data!
Replace(
index,
Encoding.GetBytes(data)
);
}
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
);
}
#region IInputStream
/* int GetHashCode() uses inherited implementation. */
public long Length
{get{return length;}}
public long Position
{
get{return position;}
set{position = (int)value;}
}
public void Read(
byte[] data
)
{
Read(
data,
0,
data.Length
);
}
public void Read(
byte[] data,
int offset,
int count
)
{
Array.Copy(
this.data,
position,
data,
offset,
length
);
position += length;
}
public int ReadByte(
)
{
// [FIX:1668291] IndexOutOfRangeException was not trapped.
try{return data[position++];}
catch{return -1;}
}
public int ReadInt(
)
{throw new NotImplementedException();}
public string ReadLine(
)
{
StringBuilder buffer = new StringBuilder();
try
{
while(true)
{
int c = data[position++];
if(c == '\r'
|| c == '\n')
break;
buffer.Append((char)c);
}
}
catch(IndexOutOfRangeException)
{throw new EndOfStreamException();}
return buffer.ToString();
}
public short ReadShort(
)
{throw new NotImplementedException();}
public string ReadString(
int length
)
{
String data = Encoding.GetString(
this.data,
position,
length
);
position += length;
return data;
}
public sbyte ReadSignedByte(
)
{
try
{return (sbyte)data[position++];}
catch(IndexOutOfRangeException)
{throw new EndOfStreamException();}
}
public ushort ReadUnsignedShort(
)
{throw new NotImplementedException();}
public void Seek(
long offset
)
{position = (int)offset;}
public void Skip(
long offset
)
{position += (int)offset;}
public byte[] ToByteArray(
)
{
byte[] data = new byte[this.length];
Array.Copy(
this.data,
0,
data,
0,
this.length
);
return data;
}
#region IDisposable
public void Dispose(
)
{}
#endregion
#endregion
#endregion
#region IOutputStream
public void Write(
byte[] data
)
{Append(data);}
public void Write(
byte[] data,
int offset,
int length
)
{Append(data,offset,length);}
public void Write(
string data
)
{Append(data);}
public void Write(
IInputStream data
)
{Append(data);}
#endregion
#endregion
#region protected
/**
<summary>Check whether the buffer has sufficient room for
adding data.</summary>
*/
protected bool 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.
)
];
Array.Copy(
this.data,
0,
data,
0,
this.length
);
this.data = data;
return true; // Reallocation happened.
}
#endregion
#endregion
#endregion
}
}
|