// dnlib: See LICENSE.txt for more info
using System;
using System.IO;
namespace dnlib.DotNet.Writer {
///
/// Writes data
///
public sealed class DataWriter {
readonly Stream stream;
readonly byte[] buffer;
const int BUFFER_LEN = 8;
internal Stream InternalStream => stream;
///
/// Gets/sets the position
///
public long Position {
get => stream.Position;
set => stream.Position = value;
}
///
/// Constructor
///
/// Destination stream
public DataWriter(Stream stream) {
if (stream is null)
ThrowArgumentNullException(nameof(stream));
this.stream = stream;
buffer = new byte[BUFFER_LEN];
}
static void ThrowArgumentNullException(string paramName) => throw new ArgumentNullException(paramName);
static void ThrowArgumentOutOfRangeException(string message) => throw new ArgumentOutOfRangeException(message);
///
/// Writes a
///
/// Value
public void WriteBoolean(bool value) => stream.WriteByte(value ? (byte)1 : (byte)0);
///
/// Writes a
///
/// Value
public void WriteSByte(sbyte value) => stream.WriteByte((byte)value);
///
/// Writes a
///
/// Value
public void WriteByte(byte value) => stream.WriteByte(value);
///
/// Writes a
///
/// Value
public void WriteInt16(short value) {
var buffer = this.buffer;
buffer[0] = (byte)value;
buffer[1] = (byte)(value >> 8);
stream.Write(buffer, 0, 2);
}
///
/// Writes a
///
/// Value
public void WriteUInt16(ushort value) {
var buffer = this.buffer;
buffer[0] = (byte)value;
buffer[1] = (byte)(value >> 8);
stream.Write(buffer, 0, 2);
}
///
/// Writes a
///
/// Value
public void WriteInt32(int value) {
var buffer = this.buffer;
buffer[0] = (byte)value;
buffer[1] = (byte)(value >> 8);
buffer[2] = (byte)(value >> 16);
buffer[3] = (byte)(value >> 24);
stream.Write(buffer, 0, 4);
}
///
/// Writes a
///
/// Value
public void WriteUInt32(uint value) {
var buffer = this.buffer;
buffer[0] = (byte)value;
buffer[1] = (byte)(value >> 8);
buffer[2] = (byte)(value >> 16);
buffer[3] = (byte)(value >> 24);
stream.Write(buffer, 0, 4);
}
///
/// Writes a
///
/// Value
public void WriteInt64(long value) {
var buffer = this.buffer;
buffer[0] = (byte)value;
buffer[1] = (byte)(value >> 8);
buffer[2] = (byte)(value >> 16);
buffer[3] = (byte)(value >> 24);
buffer[4] = (byte)(value >> 32);
buffer[5] = (byte)(value >> 40);
buffer[6] = (byte)(value >> 48);
buffer[7] = (byte)(value >> 56);
stream.Write(buffer, 0, 8);
}
///
/// Writes a
///
/// Value
public void WriteUInt64(ulong value) {
var buffer = this.buffer;
buffer[0] = (byte)value;
buffer[1] = (byte)(value >> 8);
buffer[2] = (byte)(value >> 16);
buffer[3] = (byte)(value >> 24);
buffer[4] = (byte)(value >> 32);
buffer[5] = (byte)(value >> 40);
buffer[6] = (byte)(value >> 48);
buffer[7] = (byte)(value >> 56);
stream.Write(buffer, 0, 8);
}
///
/// Writes a
///
/// Value
public unsafe void WriteSingle(float value) {
uint tmp = *(uint*)&value;
var buffer = this.buffer;
buffer[0] = (byte)tmp;
buffer[1] = (byte)(tmp >> 8);
buffer[2] = (byte)(tmp >> 16);
buffer[3] = (byte)(tmp >> 24);
stream.Write(buffer, 0, 4);
}
///
/// Writes a
///
/// Value
public unsafe void WriteDouble(double value) {
ulong tmp = *(ulong*)&value;
var buffer = this.buffer;
buffer[0] = (byte)tmp;
buffer[1] = (byte)(tmp >> 8);
buffer[2] = (byte)(tmp >> 16);
buffer[3] = (byte)(tmp >> 24);
buffer[4] = (byte)(tmp >> 32);
buffer[5] = (byte)(tmp >> 40);
buffer[6] = (byte)(tmp >> 48);
buffer[7] = (byte)(tmp >> 56);
stream.Write(buffer, 0, 8);
}
///
/// Writes bytes
///
/// Bytes to write
public void WriteBytes(byte[] source) => stream.Write(source, 0, source.Length);
///
/// Writes bytes
///
/// Bytes to write
/// Index to start copying from
/// Number of bytes to copy
public void WriteBytes(byte[] source, int index, int length) => stream.Write(source, index, length);
///
/// Writes a compressed
///
/// Value
public void WriteCompressedUInt32(uint value) {
var stream = this.stream;
if (value <= 0x7F)
stream.WriteByte((byte)value);
else if (value <= 0x3FFF) {
stream.WriteByte((byte)((value >> 8) | 0x80));
stream.WriteByte((byte)value);
}
else if (value <= 0x1FFFFFFF) {
var buffer = this.buffer;
buffer[0] = (byte)((value >> 24) | 0xC0);
buffer[1] = (byte)(value >> 16);
buffer[2] = (byte)(value >> 8);
buffer[3] = (byte)value;
stream.Write(buffer, 0, 4);
}
else
ThrowArgumentOutOfRangeException("UInt32 value can't be compressed");
}
///
/// Writes a compressed
///
///
public void WriteCompressedInt32(int value) {
var stream = this.stream;
// This is almost identical to compressing a UInt32, except that we first
// recode value so the sign bit is in bit 0. Then we compress it the same
// way a UInt32 is compressed.
uint sign = (uint)value >> 31;
if (-0x40 <= value && value <= 0x3F) {
uint v = (uint)((value & 0x3F) << 1) | sign;
stream.WriteByte((byte)v);
}
else if (-0x2000 <= value && value <= 0x1FFF) {
uint v = ((uint)(value & 0x1FFF) << 1) | sign;
stream.WriteByte((byte)((v >> 8) | 0x80));
stream.WriteByte((byte)v);
}
else if (-0x10000000 <= value && value <= 0x0FFFFFFF) {
uint v = ((uint)(value & 0x0FFFFFFF) << 1) | sign;
var buffer = this.buffer;
buffer[0] = (byte)((v >> 24) | 0xC0);
buffer[1] = (byte)(v >> 16);
buffer[2] = (byte)(v >> 8);
buffer[3] = (byte)v;
stream.Write(buffer, 0, 4);
}
else
ThrowArgumentOutOfRangeException("Int32 value can't be compressed");
}
///
/// Gets the size of a compressed , see
///
/// Value
///
public static int GetCompressedUInt32Length(uint value) {
if (value <= 0x7F)
return 1;
if (value <= 0x3FFF)
return 2;
if (value <= 0x1FFFFFFF)
return 4;
ThrowArgumentOutOfRangeException("UInt32 value can't be compressed");
return 0;
}
}
}