diff --git a/Crypto/src/bcpg/BcpgOutputStream.cs b/Crypto/src/bcpg/BcpgOutputStream.cs
new file mode 100644
index 000000000..365053e11
--- /dev/null
+++ b/Crypto/src/bcpg/BcpgOutputStream.cs
@@ -0,0 +1,394 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Basic output stream.</remarks>
+ 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
+
+ /// <summary>Create a stream representing a general packet.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ public BcpgOutputStream(
+ Stream outStr)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ this.outStr = outStr;
+ }
+
+ /// <summary>Create a stream representing an old style partial object.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">The packet tag for the object.</param>
+ public BcpgOutputStream(
+ Stream outStr,
+ PacketTag tag)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ this.outStr = outStr;
+ this.WriteHeader(tag, true, true, 0);
+ }
+
+ /// <summary>Create a stream representing a general packet.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">Packet tag.</param>
+ /// <param name="length">Size of chunks making up the packet.</param>
+ /// <param name="oldFormat">If true, the header is written out in old format.</param>
+ 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);
+ }
+ }
+
+ /// <summary>Create a new style partial input stream buffered into chunks.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">Packet tag.</param>
+ /// <param name="length">Size of chunks making up the packet.</param>
+ public BcpgOutputStream(
+ Stream outStr,
+ PacketTag tag,
+ long length)
+ {
+ if (outStr == null)
+ throw new ArgumentNullException("outStr");
+
+ this.outStr = outStr;
+ this.WriteHeader(tag, false, false, length);
+ }
+
+ /// <summary>Create a new style partial input stream buffered into chunks.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">Packet tag.</param>
+ /// <param name="buffer">Buffer to use for collecting chunks.</param>
+ 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);
+ }
+ }
+
+ /// <summary>Flush the underlying stream.</summary>
+ public override void Flush()
+ {
+ outStr.Flush();
+ }
+
+ /// <summary>Finish writing out the current packet without closing the underlying stream.</summary>
+ public void Finish()
+ {
+ if (partialBuffer != null)
+ {
+ PartialFlush(true);
+ partialBuffer = null;
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ this.Finish();
+ outStr.Flush();
+ outStr.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
|