diff options
Diffstat (limited to 'crypto/src/bcpg/BcpgOutputStream.cs')
-rw-r--r-- | crypto/src/bcpg/BcpgOutputStream.cs | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/crypto/src/bcpg/BcpgOutputStream.cs b/crypto/src/bcpg/BcpgOutputStream.cs new file mode 100644 index 000000000..204f65b50 --- /dev/null +++ b/crypto/src/bcpg/BcpgOutputStream.cs @@ -0,0 +1,390 @@ +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; + } + } + + public override void Close() + { + this.Finish(); + outStr.Flush(); + outStr.Close(); + base.Close(); + } + } +} |