You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
409 lines
11 KiB
409 lines
11 KiB
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) |
|
#pragma warning disable |
|
using System; |
|
using System.IO; |
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO; |
|
|
|
namespace BestHTTP.SecureProtocol.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); |
|
Array.Clear(partialBuffer, 0, partialBuffer.Length); |
|
partialBuffer = null; |
|
} |
|
} |
|
|
|
#if PORTABLE || NETFX_CORE |
|
protected override void Dispose(bool disposing) |
|
{ |
|
if (disposing) |
|
{ |
|
this.Finish(); |
|
outStr.Flush(); |
|
BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.Dispose(outStr); |
|
} |
|
base.Dispose(disposing); |
|
} |
|
#else |
|
public override void Close() |
|
{ |
|
this.Finish(); |
|
outStr.Flush(); |
|
BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.Dispose(outStr); |
|
base.Close(); |
|
} |
|
#endif |
|
} |
|
} |
|
#pragma warning restore |
|
#endif
|
|
|