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 PartialWrite(byte[] buffer, int offset, int count) { Streams.ValidateBufferArguments(buffer, offset, count); if (partialOffset == partialBufferLength) { PartialFlush(false); } if (count <= (partialBufferLength - partialOffset)) { Array.Copy(buffer, offset, partialBuffer, partialOffset, count); partialOffset += count; } else { int diff = partialBufferLength - partialOffset; Array.Copy(buffer, offset, partialBuffer, partialOffset, diff); offset += diff; count -= diff; PartialFlush(false); while (count > partialBufferLength) { Array.Copy(buffer, offset, partialBuffer, 0, partialBufferLength); offset += partialBufferLength; count -= partialBufferLength; PartialFlush(false); } Array.Copy(buffer, offset, partialBuffer, 0, count); partialOffset += count; } } private void PartialWriteByte(byte value) { if (partialOffset == partialBufferLength) { PartialFlush(false); } partialBuffer[partialOffset++] = value; } public override void Write(byte[] buffer, int offset, int count) { if (partialBuffer != null) { PartialWrite(buffer, offset, count); } else { outStr.Write(buffer, offset, count); } } public override void WriteByte(byte value) { if (partialBuffer != null) { PartialWriteByte(value); } else { outStr.WriteByte(value); } } // 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); Array.Clear(partialBuffer, 0, partialBuffer.Length); partialBuffer = null; } } #if PORTABLE protected override void Dispose(bool disposing) { if (disposing) { this.Finish(); outStr.Flush(); Platform.Dispose(outStr); } base.Dispose(disposing); } #else public override void Close() { this.Finish(); outStr.Flush(); Platform.Dispose(outStr); base.Close(); } #endif } }