using System; using System.IO; using Org.BouncyCastle.Utilities; 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(); Platform.Dispose(outStr); base.Close(); } } }