using System;
using System.IO;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Bcpg
{
/// Basic output stream.
public class BcpgOutputStream
: BaseOutputStream
{
internal static BcpgOutputStream Wrap(
Stream outStr)
{
if (outStr is BcpgOutputStream)
{
return (BcpgOutputStream) outStr;
}
return new BcpgOutputStream(outStr);
}
private Stream outStr;
private byte[] partialBuffer;
private int partialBufferLength;
private int partialPower;
private int partialOffset;
private const int BufferSizePower = 16; // 2^16 size buffer on long files
/// Create a stream representing a general packet.
/// Output stream to write to.
public BcpgOutputStream(
Stream outStr)
{
if (outStr == null)
throw new ArgumentNullException("outStr");
this.outStr = outStr;
}
/// Create a stream representing an old style partial object.
/// Output stream to write to.
/// The packet tag for the object.
public BcpgOutputStream(
Stream outStr,
PacketTag tag)
{
if (outStr == null)
throw new ArgumentNullException("outStr");
this.outStr = outStr;
this.WriteHeader(tag, true, true, 0);
}
/// Create a stream representing a general packet.
/// Output stream to write to.
/// Packet tag.
/// Size of chunks making up the packet.
/// If true, the header is written out in old format.
public BcpgOutputStream(
Stream outStr,
PacketTag tag,
long length,
bool oldFormat)
{
if (outStr == null)
throw new ArgumentNullException("outStr");
this.outStr = outStr;
if (length > 0xFFFFFFFFL)
{
this.WriteHeader(tag, false, true, 0);
this.partialBufferLength = 1 << BufferSizePower;
this.partialBuffer = new byte[partialBufferLength];
this.partialPower = BufferSizePower;
this.partialOffset = 0;
}
else
{
this.WriteHeader(tag, oldFormat, false, length);
}
}
/// Create a new style partial input stream buffered into chunks.
/// Output stream to write to.
/// Packet tag.
/// Size of chunks making up the packet.
public BcpgOutputStream(
Stream outStr,
PacketTag tag,
long length)
{
if (outStr == null)
throw new ArgumentNullException("outStr");
this.outStr = outStr;
this.WriteHeader(tag, false, false, length);
}
/// Create a new style partial input stream buffered into chunks.
/// Output stream to write to.
/// Packet tag.
/// Buffer to use for collecting chunks.
public BcpgOutputStream(
Stream outStr,
PacketTag tag,
byte[] buffer)
{
if (outStr == null)
throw new ArgumentNullException("outStr");
this.outStr = outStr;
this.WriteHeader(tag, false, true, 0);
this.partialBuffer = buffer;
uint length = (uint) partialBuffer.Length;
for (partialPower = 0; length != 1; partialPower++)
{
length >>= 1;
}
if (partialPower > 30)
{
throw new IOException("Buffer cannot be greater than 2^30 in length.");
}
this.partialBufferLength = 1 << partialPower;
this.partialOffset = 0;
}
private void WriteNewPacketLength(
long bodyLen)
{
if (bodyLen < 192)
{
outStr.WriteByte((byte)bodyLen);
}
else if (bodyLen <= 8383)
{
bodyLen -= 192;
outStr.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
outStr.WriteByte((byte)bodyLen);
}
else
{
outStr.WriteByte(0xff);
outStr.WriteByte((byte)(bodyLen >> 24));
outStr.WriteByte((byte)(bodyLen >> 16));
outStr.WriteByte((byte)(bodyLen >> 8));
outStr.WriteByte((byte)bodyLen);
}
}
private void WriteHeader(
PacketTag tag,
bool oldPackets,
bool partial,
long bodyLen)
{
int hdr = 0x80;
if (partialBuffer != null)
{
PartialFlush(true);
partialBuffer = null;
}
if (oldPackets)
{
hdr |= ((int) tag) << 2;
if (partial)
{
this.WriteByte((byte)(hdr | 0x03));
}
else
{
if (bodyLen <= 0xff)
{
this.WriteByte((byte) hdr);
this.WriteByte((byte)bodyLen);
}
else if (bodyLen <= 0xffff)
{
this.WriteByte((byte)(hdr | 0x01));
this.WriteByte((byte)(bodyLen >> 8));
this.WriteByte((byte)(bodyLen));
}
else
{
this.WriteByte((byte)(hdr | 0x02));
this.WriteByte((byte)(bodyLen >> 24));
this.WriteByte((byte)(bodyLen >> 16));
this.WriteByte((byte)(bodyLen >> 8));
this.WriteByte((byte)bodyLen);
}
}
}
else
{
hdr |= 0x40 | (int) tag;
this.WriteByte((byte) hdr);
if (partial)
{
partialOffset = 0;
}
else
{
this.WriteNewPacketLength(bodyLen);
}
}
}
private void PartialFlush(
bool isLast)
{
if (isLast)
{
WriteNewPacketLength(partialOffset);
outStr.Write(partialBuffer, 0, partialOffset);
}
else
{
outStr.WriteByte((byte)(0xE0 | partialPower));
outStr.Write(partialBuffer, 0, partialBufferLength);
}
partialOffset = 0;
}
private void WritePartial(
byte b)
{
if (partialOffset == partialBufferLength)
{
PartialFlush(false);
}
partialBuffer[partialOffset++] = b;
}
private void WritePartial(
byte[] buffer,
int off,
int len)
{
if (partialOffset == partialBufferLength)
{
PartialFlush(false);
}
if (len <= (partialBufferLength - partialOffset))
{
Array.Copy(buffer, off, partialBuffer, partialOffset, len);
partialOffset += len;
}
else
{
int diff = partialBufferLength - partialOffset;
Array.Copy(buffer, off, partialBuffer, partialOffset, diff);
off += diff;
len -= diff;
PartialFlush(false);
while (len > partialBufferLength)
{
Array.Copy(buffer, off, partialBuffer, 0, partialBufferLength);
off += partialBufferLength;
len -= partialBufferLength;
PartialFlush(false);
}
Array.Copy(buffer, off, partialBuffer, 0, len);
partialOffset += len;
}
}
public override void WriteByte(
byte value)
{
if (partialBuffer != null)
{
WritePartial(value);
}
else
{
outStr.WriteByte(value);
}
}
public override void Write(
byte[] buffer,
int offset,
int count)
{
if (partialBuffer != null)
{
WritePartial(buffer, offset, count);
}
else
{
outStr.Write(buffer, offset, count);
}
}
// Additional helper methods to write primitive types
internal virtual void WriteShort(
short n)
{
this.Write(
(byte)(n >> 8),
(byte)n);
}
internal virtual void WriteInt(
int n)
{
this.Write(
(byte)(n >> 24),
(byte)(n >> 16),
(byte)(n >> 8),
(byte)n);
}
internal virtual void WriteLong(
long n)
{
this.Write(
(byte)(n >> 56),
(byte)(n >> 48),
(byte)(n >> 40),
(byte)(n >> 32),
(byte)(n >> 24),
(byte)(n >> 16),
(byte)(n >> 8),
(byte)n);
}
public void WritePacket(
ContainedPacket p)
{
p.Encode(this);
}
internal void WritePacket(
PacketTag tag,
byte[] body,
bool oldFormat)
{
this.WriteHeader(tag, oldFormat, false, body.Length);
this.Write(body);
}
public void WriteObject(
BcpgObject bcpgObject)
{
bcpgObject.Encode(this);
}
public void WriteObjects(
params BcpgObject[] v)
{
foreach (BcpgObject o in v)
{
o.Encode(this);
}
}
/// Flush the underlying stream.
public override void Flush()
{
outStr.Flush();
}
/// Finish writing out the current packet without closing the underlying stream.
public void Finish()
{
if (partialBuffer != null)
{
PartialFlush(true);
partialBuffer = null;
}
}
public override void Close()
{
this.Finish();
outStr.Flush();
outStr.Close();
base.Close();
}
}
}