@ -1,2 +0,0 @@ | |||||
@echo off | |||||
escript.exe "%~dpn0" %* |
@ -0,0 +1,67 @@ | |||||
| |||||
namespace MiscUtil.Conversion | |||||
{ | |||||
/// <summary> | |||||
/// Implementation of EndianBitConverter which converts to/from big-endian | |||||
/// byte arrays. | |||||
/// </summary> | |||||
public sealed class BigEndianBitConverter : EndianBitConverter | |||||
{ | |||||
/// <summary> | |||||
/// Indicates the byte order ("endianess") in which data is converted using this class. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Different computer architectures store data using different byte orders. "Big-endian" | |||||
/// means the most significant byte is on the left end of a word. "Little-endian" means the | |||||
/// most significant byte is on the right end of a word. | |||||
/// </remarks> | |||||
/// <returns>true if this converter is little-endian, false otherwise.</returns> | |||||
public sealed override bool IsLittleEndian() | |||||
{ | |||||
return false; | |||||
} | |||||
/// <summary> | |||||
/// Indicates the byte order ("endianess") in which data is converted using this class. | |||||
/// </summary> | |||||
public sealed override Endianness Endianness | |||||
{ | |||||
get { return Endianness.BigEndian; } | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified number of bytes from value to buffer, starting at index. | |||||
/// </summary> | |||||
/// <param name="value">The value to copy</param> | |||||
/// <param name="bytes">The number of bytes to copy</param> | |||||
/// <param name="buffer">The buffer to copy the bytes into</param> | |||||
/// <param name="index">The index to start at</param> | |||||
protected override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) | |||||
{ | |||||
int endOffset = index+bytes-1; | |||||
for (int i=0; i < bytes; i++) | |||||
{ | |||||
buffer[endOffset-i] = unchecked((byte)(value&0xff)); | |||||
value = value >> 8; | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Returns a value built from the specified number of bytes from the given buffer, | |||||
/// starting at index. | |||||
/// </summary> | |||||
/// <param name="buffer">The data in byte array format</param> | |||||
/// <param name="startIndex">The first index to use</param> | |||||
/// <param name="bytesToConvert">The number of bytes to use</param> | |||||
/// <returns>The value built from the given bytes</returns> | |||||
protected override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) | |||||
{ | |||||
long ret = 0; | |||||
for (int i=0; i < bytesToConvert; i++) | |||||
{ | |||||
ret = unchecked((ret << 8) | buffer[startIndex+i]); | |||||
} | |||||
return ret; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<OutputType>Exe</OutputType> | |||||
<TargetFramework>net7.0</TargetFramework> | |||||
<ImplicitUsings>enable</ImplicitUsings> | |||||
<Nullable>enable</Nullable> | |||||
</PropertyGroup> | |||||
</Project> |
@ -0,0 +1,599 @@ | |||||
using System; | |||||
using System.IO; | |||||
using System.Text; | |||||
using MiscUtil.Conversion; | |||||
namespace MiscUtil.IO | |||||
{ | |||||
/// <summary> | |||||
/// Equivalent of System.IO.BinaryReader, but with either endianness, depending on | |||||
/// the EndianBitConverter it is constructed with. No data is buffered in the | |||||
/// reader; the client may seek within the stream at will. | |||||
/// </summary> | |||||
public class EndianBinaryReader : IDisposable | |||||
{ | |||||
#region Fields not directly related to properties | |||||
/// <summary> | |||||
/// Whether or not this reader has been disposed yet. | |||||
/// </summary> | |||||
bool disposed=false; | |||||
/// <summary> | |||||
/// Decoder to use for string conversions. | |||||
/// </summary> | |||||
Decoder decoder; | |||||
/// <summary> | |||||
/// Buffer used for temporary storage before conversion into primitives | |||||
/// </summary> | |||||
byte[] buffer = new byte[16]; | |||||
/// <summary> | |||||
/// Buffer used for temporary storage when reading a single character | |||||
/// </summary> | |||||
char[] charBuffer = new char[1]; | |||||
/// <summary> | |||||
/// Minimum number of bytes used to encode a character | |||||
/// </summary> | |||||
int minBytesPerChar; | |||||
#endregion | |||||
#region Constructors | |||||
/// <summary> | |||||
/// Equivalent of System.IO.BinaryWriter, but with either endianness, depending on | |||||
/// the EndianBitConverter it is constructed with. | |||||
/// </summary> | |||||
/// <param name="bitConverter">Converter to use when reading data</param> | |||||
/// <param name="stream">Stream to read data from</param> | |||||
public EndianBinaryReader (EndianBitConverter bitConverter, | |||||
Stream stream) : this (bitConverter, stream, Encoding.UTF8) | |||||
{ | |||||
} | |||||
/// <summary> | |||||
/// Constructs a new binary reader with the given bit converter, reading | |||||
/// to the given stream, using the given encoding. | |||||
/// </summary> | |||||
/// <param name="bitConverter">Converter to use when reading data</param> | |||||
/// <param name="stream">Stream to read data from</param> | |||||
/// <param name="encoding">Encoding to use when reading character data</param> | |||||
public EndianBinaryReader (EndianBitConverter bitConverter, Stream stream, Encoding encoding) | |||||
{ | |||||
if (bitConverter==null) | |||||
{ | |||||
throw new ArgumentNullException("bitConverter"); | |||||
} | |||||
if (stream==null) | |||||
{ | |||||
throw new ArgumentNullException("stream"); | |||||
} | |||||
if (encoding==null) | |||||
{ | |||||
throw new ArgumentNullException("encoding"); | |||||
} | |||||
if (!stream.CanRead) | |||||
{ | |||||
throw new ArgumentException("Stream isn't writable", "stream"); | |||||
} | |||||
this.stream = stream; | |||||
this.bitConverter = bitConverter; | |||||
this.encoding = encoding; | |||||
this.decoder = encoding.GetDecoder(); | |||||
this.minBytesPerChar = 1; | |||||
if (encoding is UnicodeEncoding) | |||||
{ | |||||
minBytesPerChar = 2; | |||||
} | |||||
} | |||||
#endregion | |||||
#region Properties | |||||
EndianBitConverter bitConverter; | |||||
/// <summary> | |||||
/// The bit converter used to read values from the stream | |||||
/// </summary> | |||||
public EndianBitConverter BitConverter | |||||
{ | |||||
get { return bitConverter; } | |||||
} | |||||
Encoding encoding; | |||||
/// <summary> | |||||
/// The encoding used to read strings | |||||
/// </summary> | |||||
public Encoding Encoding | |||||
{ | |||||
get { return encoding; } | |||||
} | |||||
Stream stream; | |||||
/// <summary> | |||||
/// Gets the underlying stream of the EndianBinaryReader. | |||||
/// </summary> | |||||
public Stream BaseStream | |||||
{ | |||||
get { return stream; } | |||||
} | |||||
#endregion | |||||
#region Public methods | |||||
/// <summary> | |||||
/// Closes the reader, including the underlying stream.. | |||||
/// </summary> | |||||
public void Close() | |||||
{ | |||||
Dispose(); | |||||
} | |||||
/// <summary> | |||||
/// Seeks within the stream. | |||||
/// </summary> | |||||
/// <param name="offset">Offset to seek to.</param> | |||||
/// <param name="origin">Origin of seek operation.</param> | |||||
public void Seek (int offset, SeekOrigin origin) | |||||
{ | |||||
CheckDisposed(); | |||||
stream.Seek (offset, origin); | |||||
} | |||||
/// <summary> | |||||
/// Reads a single byte from the stream. | |||||
/// </summary> | |||||
/// <returns>The byte read</returns> | |||||
public byte ReadByte() | |||||
{ | |||||
ReadInternal(buffer, 1); | |||||
return buffer[0]; | |||||
} | |||||
/// <summary> | |||||
/// Reads a single signed byte from the stream. | |||||
/// </summary> | |||||
/// <returns>The byte read</returns> | |||||
public sbyte ReadSByte() | |||||
{ | |||||
ReadInternal(buffer, 1); | |||||
return unchecked((sbyte)buffer[0]); | |||||
} | |||||
/// <summary> | |||||
/// Reads a boolean from the stream. 1 byte is read. | |||||
/// </summary> | |||||
/// <returns>The boolean read</returns> | |||||
public bool ReadBoolean() | |||||
{ | |||||
ReadInternal(buffer, 1); | |||||
return bitConverter.ToBoolean(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a 16-bit signed integer from the stream, using the bit converter | |||||
/// for this reader. 2 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The 16-bit integer read</returns> | |||||
public short ReadInt16() | |||||
{ | |||||
ReadInternal(buffer, 2); | |||||
return bitConverter.ToInt16(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a 32-bit signed integer from the stream, using the bit converter | |||||
/// for this reader. 4 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The 32-bit integer read</returns> | |||||
public int ReadInt32() | |||||
{ | |||||
ReadInternal(buffer, 4); | |||||
return bitConverter.ToInt32(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a 64-bit signed integer from the stream, using the bit converter | |||||
/// for this reader. 8 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The 64-bit integer read</returns> | |||||
public long ReadInt64() | |||||
{ | |||||
ReadInternal(buffer, 8); | |||||
return bitConverter.ToInt64(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a 16-bit unsigned integer from the stream, using the bit converter | |||||
/// for this reader. 2 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The 16-bit unsigned integer read</returns> | |||||
public ushort ReadUInt16() | |||||
{ | |||||
ReadInternal(buffer, 2); | |||||
return bitConverter.ToUInt16(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a 32-bit unsigned integer from the stream, using the bit converter | |||||
/// for this reader. 4 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The 32-bit unsigned integer read</returns> | |||||
public uint ReadUInt32() | |||||
{ | |||||
ReadInternal(buffer, 4); | |||||
return bitConverter.ToUInt32(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a 64-bit unsigned integer from the stream, using the bit converter | |||||
/// for this reader. 8 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The 64-bit unsigned integer read</returns> | |||||
public ulong ReadUInt64() | |||||
{ | |||||
ReadInternal(buffer, 8); | |||||
return bitConverter.ToUInt64(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a single-precision floating-point value from the stream, using the bit converter | |||||
/// for this reader. 4 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The floating point value read</returns> | |||||
public float ReadSingle() | |||||
{ | |||||
ReadInternal(buffer, 4); | |||||
return bitConverter.ToSingle(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a double-precision floating-point value from the stream, using the bit converter | |||||
/// for this reader. 8 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The floating point value read</returns> | |||||
public double ReadDouble() | |||||
{ | |||||
ReadInternal(buffer, 8); | |||||
return bitConverter.ToDouble(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a decimal value from the stream, using the bit converter | |||||
/// for this reader. 16 bytes are read. | |||||
/// </summary> | |||||
/// <returns>The decimal value read</returns> | |||||
public decimal ReadDecimal() | |||||
{ | |||||
ReadInternal(buffer, 16); | |||||
return bitConverter.ToDecimal(buffer, 0); | |||||
} | |||||
/// <summary> | |||||
/// Reads a single character from the stream, using the character encoding for | |||||
/// this reader. If no characters have been fully read by the time the stream ends, | |||||
/// -1 is returned. | |||||
/// </summary> | |||||
/// <returns>The character read, or -1 for end of stream.</returns> | |||||
public int Read() | |||||
{ | |||||
int charsRead = Read(charBuffer, 0, 1); | |||||
if (charsRead==0) | |||||
{ | |||||
return -1; | |||||
} | |||||
else | |||||
{ | |||||
return charBuffer[0]; | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Reads the specified number of characters into the given buffer, starting at | |||||
/// the given index. | |||||
/// </summary> | |||||
/// <param name="data">The buffer to copy data into</param> | |||||
/// <param name="index">The first index to copy data into</param> | |||||
/// <param name="count">The number of characters to read</param> | |||||
/// <returns>The number of characters actually read. This will only be less than | |||||
/// the requested number of characters if the end of the stream is reached. | |||||
/// </returns> | |||||
public int Read(char[] data, int index, int count) | |||||
{ | |||||
CheckDisposed(); | |||||
if (buffer==null) | |||||
{ | |||||
throw new ArgumentNullException("buffer"); | |||||
} | |||||
if (index < 0) | |||||
{ | |||||
throw new ArgumentOutOfRangeException("index"); | |||||
} | |||||
if (count < 0) | |||||
{ | |||||
throw new ArgumentOutOfRangeException("index"); | |||||
} | |||||
if (count+index > data.Length) | |||||
{ | |||||
throw new ArgumentException | |||||
("Not enough space in buffer for specified number of characters starting at specified index"); | |||||
} | |||||
int read=0; | |||||
bool firstTime=true; | |||||
// Use the normal buffer if we're only reading a small amount, otherwise | |||||
// use at most 4K at a time. | |||||
byte[] byteBuffer = buffer; | |||||
if (byteBuffer.Length < count*minBytesPerChar) | |||||
{ | |||||
byteBuffer = new byte[4096]; | |||||
} | |||||
while (read < count) | |||||
{ | |||||
int amountToRead; | |||||
// First time through we know we haven't previously read any data | |||||
if (firstTime) | |||||
{ | |||||
amountToRead = count*minBytesPerChar; | |||||
firstTime=false; | |||||
} | |||||
// After that we can only assume we need to fully read "chars left -1" characters | |||||
// and a single byte of the character we may be in the middle of | |||||
else | |||||
{ | |||||
amountToRead = ((count-read-1)*minBytesPerChar)+1; | |||||
} | |||||
if (amountToRead > byteBuffer.Length) | |||||
{ | |||||
amountToRead = byteBuffer.Length; | |||||
} | |||||
int bytesRead = TryReadInternal(byteBuffer, amountToRead); | |||||
if (bytesRead==0) | |||||
{ | |||||
return read; | |||||
} | |||||
int decoded = decoder.GetChars(byteBuffer, 0, bytesRead, data, index); | |||||
read += decoded; | |||||
index += decoded; | |||||
} | |||||
return read; | |||||
} | |||||
/// <summary> | |||||
/// Reads the specified number of bytes into the given buffer, starting at | |||||
/// the given index. | |||||
/// </summary> | |||||
/// <param name="buffer">The buffer to copy data into</param> | |||||
/// <param name="index">The first index to copy data into</param> | |||||
/// <param name="count">The number of bytes to read</param> | |||||
/// <returns>The number of bytes actually read. This will only be less than | |||||
/// the requested number of bytes if the end of the stream is reached. | |||||
/// </returns> | |||||
public int Read(byte[] buffer, int index, int count) | |||||
{ | |||||
CheckDisposed(); | |||||
if (buffer==null) | |||||
{ | |||||
throw new ArgumentNullException("buffer"); | |||||
} | |||||
if (index < 0) | |||||
{ | |||||
throw new ArgumentOutOfRangeException("index"); | |||||
} | |||||
if (count < 0) | |||||
{ | |||||
throw new ArgumentOutOfRangeException("index"); | |||||
} | |||||
if (count+index > buffer.Length) | |||||
{ | |||||
throw new ArgumentException | |||||
("Not enough space in buffer for specified number of bytes starting at specified index"); | |||||
} | |||||
int read=0; | |||||
while (count > 0) | |||||
{ | |||||
int block = stream.Read(buffer, index, count); | |||||
if (block==0) | |||||
{ | |||||
return read; | |||||
} | |||||
index += block; | |||||
read += block; | |||||
count -= block; | |||||
} | |||||
return read; | |||||
} | |||||
/// <summary> | |||||
/// Reads the specified number of bytes, returning them in a new byte array. | |||||
/// If not enough bytes are available before the end of the stream, this | |||||
/// method will return what is available. | |||||
/// </summary> | |||||
/// <param name="count">The number of bytes to read</param> | |||||
/// <returns>The bytes read</returns> | |||||
public byte[] ReadBytes(int count) | |||||
{ | |||||
CheckDisposed(); | |||||
if (count < 0) | |||||
{ | |||||
throw new ArgumentOutOfRangeException("count"); | |||||
} | |||||
byte[] ret = new byte[count]; | |||||
int index=0; | |||||
while (index < count) | |||||
{ | |||||
int read = stream.Read(ret, index, count-index); | |||||
// Stream has finished half way through. That's fine, return what we've got. | |||||
if (read==0) | |||||
{ | |||||
byte[] copy = new byte[index]; | |||||
Buffer.BlockCopy(ret, 0, copy, 0, index); | |||||
return copy; | |||||
} | |||||
index += read; | |||||
} | |||||
return ret; | |||||
} | |||||
/// <summary> | |||||
/// Reads the specified number of bytes, returning them in a new byte array. | |||||
/// If not enough bytes are available before the end of the stream, this | |||||
/// method will throw an IOException. | |||||
/// </summary> | |||||
/// <param name="count">The number of bytes to read</param> | |||||
/// <returns>The bytes read</returns> | |||||
public byte[] ReadBytesOrThrow(int count) | |||||
{ | |||||
byte[] ret = new byte[count]; | |||||
ReadInternal(ret, count); | |||||
return ret; | |||||
} | |||||
/// <summary> | |||||
/// Reads a 7-bit encoded integer from the stream. This is stored with the least significant | |||||
/// information first, with 7 bits of information per byte of value, and the top | |||||
/// bit as a continuation flag. This method is not affected by the endianness | |||||
/// of the bit converter. | |||||
/// </summary> | |||||
/// <returns>The 7-bit encoded integer read from the stream.</returns> | |||||
public int Read7BitEncodedInt() | |||||
{ | |||||
CheckDisposed(); | |||||
int ret=0; | |||||
for (int shift = 0; shift < 35; shift+=7) | |||||
{ | |||||
int b = stream.ReadByte(); | |||||
if (b==-1) | |||||
{ | |||||
throw new EndOfStreamException(); | |||||
} | |||||
ret = ret | ((b&0x7f) << shift); | |||||
if ((b & 0x80) == 0) | |||||
{ | |||||
return ret; | |||||
} | |||||
} | |||||
// Still haven't seen a byte with the high bit unset? Dodgy data. | |||||
throw new IOException("Invalid 7-bit encoded integer in stream."); | |||||
} | |||||
/// <summary> | |||||
/// Reads a 7-bit encoded integer from the stream. This is stored with the most significant | |||||
/// information first, with 7 bits of information per byte of value, and the top | |||||
/// bit as a continuation flag. This method is not affected by the endianness | |||||
/// of the bit converter. | |||||
/// </summary> | |||||
/// <returns>The 7-bit encoded integer read from the stream.</returns> | |||||
public int ReadBigEndian7BitEncodedInt() | |||||
{ | |||||
CheckDisposed(); | |||||
int ret=0; | |||||
for (int i=0; i < 5; i++) | |||||
{ | |||||
int b = stream.ReadByte(); | |||||
if (b==-1) | |||||
{ | |||||
throw new EndOfStreamException(); | |||||
} | |||||
ret = (ret << 7) | (b&0x7f); | |||||
if ((b & 0x80) == 0) | |||||
{ | |||||
return ret; | |||||
} | |||||
} | |||||
// Still haven't seen a byte with the high bit unset? Dodgy data. | |||||
throw new IOException("Invalid 7-bit encoded integer in stream."); | |||||
} | |||||
/// <summary> | |||||
/// Reads a length-prefixed string from the stream, using the encoding for this reader. | |||||
/// A 7-bit encoded integer is first read, which specifies the number of bytes | |||||
/// to read from the stream. These bytes are then converted into a string with | |||||
/// the encoding for this reader. | |||||
/// </summary> | |||||
/// <returns>The string read from the stream.</returns> | |||||
public string ReadString() | |||||
{ | |||||
int bytesToRead = Read7BitEncodedInt(); | |||||
byte[] data = new byte[bytesToRead]; | |||||
ReadInternal(data, bytesToRead); | |||||
return encoding.GetString(data, 0, data.Length); | |||||
} | |||||
#endregion | |||||
#region Private methods | |||||
/// <summary> | |||||
/// Checks whether or not the reader has been disposed, throwing an exception if so. | |||||
/// </summary> | |||||
void CheckDisposed() | |||||
{ | |||||
if (disposed) | |||||
{ | |||||
throw new ObjectDisposedException("EndianBinaryReader"); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Reads the given number of bytes from the stream, throwing an exception | |||||
/// if they can't all be read. | |||||
/// </summary> | |||||
/// <param name="data">Buffer to read into</param> | |||||
/// <param name="size">Number of bytes to read</param> | |||||
void ReadInternal (byte[] data, int size) | |||||
{ | |||||
CheckDisposed(); | |||||
int index=0; | |||||
while (index < size) | |||||
{ | |||||
int read = stream.Read(data, index, size-index); | |||||
if (read==0) | |||||
{ | |||||
throw new EndOfStreamException | |||||
(String.Format("End of stream reached with {0} byte{1} left to read.", size-index, | |||||
size-index==1 ? "s" : "")); | |||||
} | |||||
index += read; | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Reads the given number of bytes from the stream if possible, returning | |||||
/// the number of bytes actually read, which may be less than requested if | |||||
/// (and only if) the end of the stream is reached. | |||||
/// </summary> | |||||
/// <param name="data">Buffer to read into</param> | |||||
/// <param name="size">Number of bytes to read</param> | |||||
/// <returns>Number of bytes actually read</returns> | |||||
int TryReadInternal (byte[] data, int size) | |||||
{ | |||||
CheckDisposed(); | |||||
int index=0; | |||||
while (index < size) | |||||
{ | |||||
int read = stream.Read(data, index, size-index); | |||||
if (read==0) | |||||
{ | |||||
return index; | |||||
} | |||||
index += read; | |||||
} | |||||
return index; | |||||
} | |||||
#endregion | |||||
#region IDisposable Members | |||||
/// <summary> | |||||
/// Disposes of the underlying stream. | |||||
/// </summary> | |||||
public void Dispose() | |||||
{ | |||||
if (!disposed) | |||||
{ | |||||
disposed = true; | |||||
((IDisposable)stream).Dispose(); | |||||
} | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@ -0,0 +1,392 @@ | |||||
using System; | |||||
using System.IO; | |||||
using System.Text; | |||||
using MiscUtil.Conversion; | |||||
namespace MiscUtil.IO | |||||
{ | |||||
/// <summary> | |||||
/// Equivalent of System.IO.BinaryWriter, but with either endianness, depending on | |||||
/// the EndianBitConverter it is constructed with. | |||||
/// </summary> | |||||
public class EndianBinaryWriter : IDisposable | |||||
{ | |||||
#region Fields not directly related to properties | |||||
/// <summary> | |||||
/// Whether or not this writer has been disposed yet. | |||||
/// </summary> | |||||
bool disposed=false; | |||||
/// <summary> | |||||
/// Buffer used for temporary storage during conversion from primitives | |||||
/// </summary> | |||||
byte[] buffer = new byte[16]; | |||||
/// <summary> | |||||
/// Buffer used for Write(char) | |||||
/// </summary> | |||||
char[] charBuffer = new char[1]; | |||||
#endregion | |||||
#region Constructors | |||||
/// <summary> | |||||
/// Constructs a new binary writer with the given bit converter, writing | |||||
/// to the given stream, using UTF-8 encoding. | |||||
/// </summary> | |||||
/// <param name="bitConverter">Converter to use when writing data</param> | |||||
/// <param name="stream">Stream to write data to</param> | |||||
public EndianBinaryWriter (EndianBitConverter bitConverter, | |||||
Stream stream) : this (bitConverter, stream, Encoding.UTF8) | |||||
{ | |||||
} | |||||
/// <summary> | |||||
/// Constructs a new binary writer with the given bit converter, writing | |||||
/// to the given stream, using the given encoding. | |||||
/// </summary> | |||||
/// <param name="bitConverter">Converter to use when writing data</param> | |||||
/// <param name="stream">Stream to write data to</param> | |||||
/// <param name="encoding">Encoding to use when writing character data</param> | |||||
public EndianBinaryWriter (EndianBitConverter bitConverter, Stream stream, Encoding encoding) | |||||
{ | |||||
if (bitConverter==null) | |||||
{ | |||||
throw new ArgumentNullException("bitConverter"); | |||||
} | |||||
if (stream==null) | |||||
{ | |||||
throw new ArgumentNullException("stream"); | |||||
} | |||||
if (encoding==null) | |||||
{ | |||||
throw new ArgumentNullException("encoding"); | |||||
} | |||||
if (!stream.CanWrite) | |||||
{ | |||||
throw new ArgumentException("Stream isn't writable", "stream"); | |||||
} | |||||
this.stream = stream; | |||||
this.bitConverter = bitConverter; | |||||
this.encoding = encoding; | |||||
} | |||||
#endregion | |||||
#region Properties | |||||
EndianBitConverter bitConverter; | |||||
/// <summary> | |||||
/// The bit converter used to write values to the stream | |||||
/// </summary> | |||||
public EndianBitConverter BitConverter | |||||
{ | |||||
get { return bitConverter; } | |||||
} | |||||
Encoding encoding; | |||||
/// <summary> | |||||
/// The encoding used to write strings | |||||
/// </summary> | |||||
public Encoding Encoding | |||||
{ | |||||
get { return encoding; } | |||||
} | |||||
Stream stream; | |||||
/// <summary> | |||||
/// Gets the underlying stream of the EndianBinaryWriter. | |||||
/// </summary> | |||||
public Stream BaseStream | |||||
{ | |||||
get { return stream; } | |||||
} | |||||
#endregion | |||||
#region Public methods | |||||
/// <summary> | |||||
/// Closes the writer, including the underlying stream. | |||||
/// </summary> | |||||
public void Close() | |||||
{ | |||||
Dispose(); | |||||
} | |||||
/// <summary> | |||||
/// Flushes the underlying stream. | |||||
/// </summary> | |||||
public void Flush() | |||||
{ | |||||
CheckDisposed(); | |||||
stream.Flush(); | |||||
} | |||||
/// <summary> | |||||
/// Seeks within the stream. | |||||
/// </summary> | |||||
/// <param name="offset">Offset to seek to.</param> | |||||
/// <param name="origin">Origin of seek operation.</param> | |||||
public void Seek (int offset, SeekOrigin origin) | |||||
{ | |||||
CheckDisposed(); | |||||
stream.Seek (offset, origin); | |||||
} | |||||
/// <summary> | |||||
/// Writes a boolean value to the stream. 1 byte is written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (bool value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 1); | |||||
} | |||||
/// <summary> | |||||
/// Writes a 16-bit signed integer to the stream, using the bit converter | |||||
/// for this writer. 2 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (short value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 2); | |||||
} | |||||
/// <summary> | |||||
/// Writes a 32-bit signed integer to the stream, using the bit converter | |||||
/// for this writer. 4 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (int value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 4); | |||||
} | |||||
/// <summary> | |||||
/// Writes a 64-bit signed integer to the stream, using the bit converter | |||||
/// for this writer. 8 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (long value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 8); | |||||
} | |||||
/// <summary> | |||||
/// Writes a 16-bit unsigned integer to the stream, using the bit converter | |||||
/// for this writer. 2 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (ushort value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 2); | |||||
} | |||||
/// <summary> | |||||
/// Writes a 32-bit unsigned integer to the stream, using the bit converter | |||||
/// for this writer. 4 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (uint value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 4); | |||||
} | |||||
/// <summary> | |||||
/// Writes a 64-bit unsigned integer to the stream, using the bit converter | |||||
/// for this writer. 8 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (ulong value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 8); | |||||
} | |||||
/// <summary> | |||||
/// Writes a single-precision floating-point value to the stream, using the bit converter | |||||
/// for this writer. 4 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (float value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 4); | |||||
} | |||||
/// <summary> | |||||
/// Writes a double-precision floating-point value to the stream, using the bit converter | |||||
/// for this writer. 8 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (double value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 8); | |||||
} | |||||
/// <summary> | |||||
/// Writes a decimal value to the stream, using the bit converter for this writer. | |||||
/// 16 bytes are written. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (decimal value) | |||||
{ | |||||
bitConverter.CopyBytes(value, buffer, 0); | |||||
WriteInternal(buffer, 16); | |||||
} | |||||
/// <summary> | |||||
/// Writes a signed byte to the stream. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (byte value) | |||||
{ | |||||
buffer[0] = value; | |||||
WriteInternal(buffer, 1); | |||||
} | |||||
/// <summary> | |||||
/// Writes an unsigned byte to the stream. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write (sbyte value) | |||||
{ | |||||
buffer[0] = unchecked((byte)value); | |||||
WriteInternal(buffer, 1); | |||||
} | |||||
/// <summary> | |||||
/// Writes an array of bytes to the stream. | |||||
/// </summary> | |||||
/// <param name="value">The values to write</param> | |||||
public void Write (byte[] value) | |||||
{ | |||||
if (value == null) | |||||
{ | |||||
throw (new System.ArgumentNullException("value")); | |||||
} | |||||
WriteInternal(value, value.Length); | |||||
} | |||||
/// <summary> | |||||
/// Writes a portion of an array of bytes to the stream. | |||||
/// </summary> | |||||
/// <param name="value">An array containing the bytes to write</param> | |||||
/// <param name="offset">The index of the first byte to write within the array</param> | |||||
/// <param name="count">The number of bytes to write</param> | |||||
public void Write (byte[] value, int offset, int count) | |||||
{ | |||||
CheckDisposed(); | |||||
stream.Write(value, offset, count); | |||||
} | |||||
/// <summary> | |||||
/// Writes a single character to the stream, using the encoding for this writer. | |||||
/// </summary> | |||||
/// <param name="value">The value to write</param> | |||||
public void Write(char value) | |||||
{ | |||||
charBuffer[0] = value; | |||||
Write(charBuffer); | |||||
} | |||||
/// <summary> | |||||
/// Writes an array of characters to the stream, using the encoding for this writer. | |||||
/// </summary> | |||||
/// <param name="value">An array containing the characters to write</param> | |||||
public void Write(char[] value) | |||||
{ | |||||
if (value==null) | |||||
{ | |||||
throw new ArgumentNullException("value"); | |||||
} | |||||
CheckDisposed(); | |||||
byte[] data = Encoding.GetBytes(value, 0, value.Length); | |||||
WriteInternal(data, data.Length); | |||||
} | |||||
/// <summary> | |||||
/// Writes a string to the stream, using the encoding for this writer. | |||||
/// </summary> | |||||
/// <param name="value">The value to write. Must not be null.</param> | |||||
/// <exception cref="ArgumentNullException">value is null</exception> | |||||
public void Write(string value) | |||||
{ | |||||
if (value==null) | |||||
{ | |||||
throw new ArgumentNullException("value"); | |||||
} | |||||
CheckDisposed(); | |||||
byte[] data = Encoding.GetBytes(value); | |||||
Write7BitEncodedInt(data.Length); | |||||
WriteInternal(data, data.Length); | |||||
} | |||||
/// <summary> | |||||
/// Writes a 7-bit encoded integer from the stream. This is stored with the least significant | |||||
/// information first, with 7 bits of information per byte of value, and the top | |||||
/// bit as a continuation flag. | |||||
/// </summary> | |||||
/// <param name="value">The 7-bit encoded integer to write to the stream</param> | |||||
public void Write7BitEncodedInt(int value) | |||||
{ | |||||
CheckDisposed(); | |||||
if (value < 0) | |||||
{ | |||||
throw new ArgumentOutOfRangeException("value", "Value must be greater than or equal to 0."); | |||||
} | |||||
int index=0; | |||||
while (value >= 128) | |||||
{ | |||||
buffer[index++]= (byte)((value&0x7f) | 0x80); | |||||
value = value >> 7; | |||||
index++; | |||||
} | |||||
buffer[index++]=(byte)value; | |||||
stream.Write(buffer, 0, index); | |||||
} | |||||
#endregion | |||||
#region Private methods | |||||
/// <summary> | |||||
/// Checks whether or not the writer has been disposed, throwing an exception if so. | |||||
/// </summary> | |||||
void CheckDisposed() | |||||
{ | |||||
if (disposed) | |||||
{ | |||||
throw new ObjectDisposedException("EndianBinaryWriter"); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Writes the specified number of bytes from the start of the given byte array, | |||||
/// after checking whether or not the writer has been disposed. | |||||
/// </summary> | |||||
/// <param name="bytes">The array of bytes to write from</param> | |||||
/// <param name="length">The number of bytes to write</param> | |||||
void WriteInternal (byte[] bytes, int length) | |||||
{ | |||||
CheckDisposed(); | |||||
stream.Write(bytes, 0, length); | |||||
} | |||||
#endregion | |||||
#region IDisposable Members | |||||
/// <summary> | |||||
/// Disposes of the underlying stream. | |||||
/// </summary> | |||||
public void Dispose() | |||||
{ | |||||
if (!disposed) | |||||
{ | |||||
Flush(); | |||||
disposed = true; | |||||
((IDisposable)stream).Dispose(); | |||||
} | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@ -0,0 +1,696 @@ | |||||
using System; | |||||
using System.Runtime.InteropServices; | |||||
namespace MiscUtil.Conversion | |||||
{ | |||||
/// <summary> | |||||
/// Equivalent of System.BitConverter, but with either endianness. | |||||
/// </summary> | |||||
public abstract class EndianBitConverter | |||||
{ | |||||
#region Endianness of this converter | |||||
/// <summary> | |||||
/// Indicates the byte order ("endianess") in which data is converted using this class. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Different computer architectures store data using different byte orders. "Big-endian" | |||||
/// means the most significant byte is on the left end of a word. "Little-endian" means the | |||||
/// most significant byte is on the right end of a word. | |||||
/// </remarks> | |||||
/// <returns>true if this converter is little-endian, false otherwise.</returns> | |||||
public abstract bool IsLittleEndian(); | |||||
/// <summary> | |||||
/// Indicates the byte order ("endianess") in which data is converted using this class. | |||||
/// </summary> | |||||
public abstract Endianness Endianness { get; } | |||||
#endregion | |||||
#region Factory properties | |||||
static LittleEndianBitConverter little = new LittleEndianBitConverter(); | |||||
/// <summary> | |||||
/// Returns a little-endian bit converter instance. The same instance is | |||||
/// always returned. | |||||
/// </summary> | |||||
public static LittleEndianBitConverter Little | |||||
{ | |||||
get { return little; } | |||||
} | |||||
static BigEndianBitConverter big = new BigEndianBitConverter(); | |||||
/// <summary> | |||||
/// Returns a big-endian bit converter instance. The same instance is | |||||
/// always returned. | |||||
/// </summary> | |||||
public static BigEndianBitConverter Big | |||||
{ | |||||
get { return big; } | |||||
} | |||||
#endregion | |||||
#region Double/primitive conversions | |||||
/// <summary> | |||||
/// Converts the specified double-precision floating point number to a | |||||
/// 64-bit signed integer. Note: the endianness of this converter does not | |||||
/// affect the returned value. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert. </param> | |||||
/// <returns>A 64-bit signed integer whose value is equivalent to value.</returns> | |||||
public long DoubleToInt64Bits(double value) | |||||
{ | |||||
return BitConverter.DoubleToInt64Bits(value); | |||||
} | |||||
/// <summary> | |||||
/// Converts the specified 64-bit signed integer to a double-precision | |||||
/// floating point number. Note: the endianness of this converter does not | |||||
/// affect the returned value. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert. </param> | |||||
/// <returns>A double-precision floating point number whose value is equivalent to value.</returns> | |||||
public double Int64BitsToDouble (long value) | |||||
{ | |||||
return BitConverter.Int64BitsToDouble(value); | |||||
} | |||||
/// <summary> | |||||
/// Converts the specified single-precision floating point number to a | |||||
/// 32-bit signed integer. Note: the endianness of this converter does not | |||||
/// affect the returned value. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert. </param> | |||||
/// <returns>A 32-bit signed integer whose value is equivalent to value.</returns> | |||||
public int SingleToInt32Bits(float value) | |||||
{ | |||||
return new Int32SingleUnion(value).AsInt32; | |||||
} | |||||
/// <summary> | |||||
/// Converts the specified 32-bit signed integer to a single-precision floating point | |||||
/// number. Note: the endianness of this converter does not | |||||
/// affect the returned value. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert. </param> | |||||
/// <returns>A single-precision floating point number whose value is equivalent to value.</returns> | |||||
public float Int32BitsToSingle (int value) | |||||
{ | |||||
return new Int32SingleUnion(value).AsSingle; | |||||
} | |||||
#endregion | |||||
#region To(PrimitiveType) conversions | |||||
/// <summary> | |||||
/// Returns a Boolean value converted from one byte at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>true if the byte at startIndex in value is nonzero; otherwise, false.</returns> | |||||
public bool ToBoolean (byte[] value, int startIndex) | |||||
{ | |||||
CheckByteArgument(value, startIndex, 1); | |||||
return BitConverter.ToBoolean(value, startIndex); | |||||
} | |||||
/// <summary> | |||||
/// Returns a Unicode character converted from two bytes at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A character formed by two bytes beginning at startIndex.</returns> | |||||
public char ToChar (byte[] value, int startIndex) | |||||
{ | |||||
return unchecked((char) (CheckedFromBytes(value, startIndex, 2))); | |||||
} | |||||
/// <summary> | |||||
/// Returns a double-precision floating point number converted from eight bytes | |||||
/// at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A double precision floating point number formed by eight bytes beginning at startIndex.</returns> | |||||
public double ToDouble (byte[] value, int startIndex) | |||||
{ | |||||
return Int64BitsToDouble(ToInt64(value, startIndex)); | |||||
} | |||||
/// <summary> | |||||
/// Returns a single-precision floating point number converted from four bytes | |||||
/// at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A single precision floating point number formed by four bytes beginning at startIndex.</returns> | |||||
public float ToSingle (byte[] value, int startIndex) | |||||
{ | |||||
return Int32BitsToSingle(ToInt32(value, startIndex)); | |||||
} | |||||
/// <summary> | |||||
/// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A 16-bit signed integer formed by two bytes beginning at startIndex.</returns> | |||||
public short ToInt16 (byte[] value, int startIndex) | |||||
{ | |||||
return unchecked((short) (CheckedFromBytes(value, startIndex, 2))); | |||||
} | |||||
/// <summary> | |||||
/// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A 32-bit signed integer formed by four bytes beginning at startIndex.</returns> | |||||
public int ToInt32 (byte[] value, int startIndex) | |||||
{ | |||||
return unchecked((int) (CheckedFromBytes(value, startIndex, 4))); | |||||
} | |||||
/// <summary> | |||||
/// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A 64-bit signed integer formed by eight bytes beginning at startIndex.</returns> | |||||
public long ToInt64 (byte[] value, int startIndex) | |||||
{ | |||||
return CheckedFromBytes(value, startIndex, 8); | |||||
} | |||||
/// <summary> | |||||
/// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A 16-bit unsigned integer formed by two bytes beginning at startIndex.</returns> | |||||
public ushort ToUInt16 (byte[] value, int startIndex) | |||||
{ | |||||
return unchecked((ushort) (CheckedFromBytes(value, startIndex, 2))); | |||||
} | |||||
/// <summary> | |||||
/// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A 32-bit unsigned integer formed by four bytes beginning at startIndex.</returns> | |||||
public uint ToUInt32 (byte[] value, int startIndex) | |||||
{ | |||||
return unchecked((uint) (CheckedFromBytes(value, startIndex, 4))); | |||||
} | |||||
/// <summary> | |||||
/// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A 64-bit unsigned integer formed by eight bytes beginning at startIndex.</returns> | |||||
public ulong ToUInt64 (byte[] value, int startIndex) | |||||
{ | |||||
return unchecked((ulong) (CheckedFromBytes(value, startIndex, 8))); | |||||
} | |||||
/// <summary> | |||||
/// Checks the given argument for validity. | |||||
/// </summary> | |||||
/// <param name="value">The byte array passed in</param> | |||||
/// <param name="startIndex">The start index passed in</param> | |||||
/// <param name="bytesRequired">The number of bytes required</param> | |||||
/// <exception cref="ArgumentNullException">value is a null reference</exception> | |||||
/// <exception cref="ArgumentOutOfRangeException"> | |||||
/// startIndex is less than zero or greater than the length of value minus bytesRequired. | |||||
/// </exception> | |||||
static void CheckByteArgument(byte[] value, int startIndex, int bytesRequired) | |||||
{ | |||||
if (value==null) | |||||
{ | |||||
throw new ArgumentNullException("value"); | |||||
} | |||||
if (startIndex < 0 || startIndex > value.Length-bytesRequired) | |||||
{ | |||||
throw new ArgumentOutOfRangeException("startIndex"); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Checks the arguments for validity before calling FromBytes | |||||
/// (which can therefore assume the arguments are valid). | |||||
/// </summary> | |||||
/// <param name="value">The bytes to convert after checking</param> | |||||
/// <param name="startIndex">The index of the first byte to convert</param> | |||||
/// <param name="bytesToConvert">The number of bytes to convert</param> | |||||
/// <returns></returns> | |||||
long CheckedFromBytes(byte[] value, int startIndex, int bytesToConvert) | |||||
{ | |||||
CheckByteArgument(value, startIndex, bytesToConvert); | |||||
return FromBytes(value, startIndex, bytesToConvert); | |||||
} | |||||
/// <summary> | |||||
/// Convert the given number of bytes from the given array, from the given start | |||||
/// position, into a long, using the bytes as the least significant part of the long. | |||||
/// By the time this is called, the arguments have been checked for validity. | |||||
/// </summary> | |||||
/// <param name="value">The bytes to convert</param> | |||||
/// <param name="startIndex">The index of the first byte to convert</param> | |||||
/// <param name="bytesToConvert">The number of bytes to use in the conversion</param> | |||||
/// <returns>The converted number</returns> | |||||
protected abstract long FromBytes(byte[] value, int startIndex, int bytesToConvert); | |||||
#endregion | |||||
#region ToString conversions | |||||
/// <summary> | |||||
/// Returns a String converted from the elements of a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <remarks>All the elements of value are converted.</remarks> | |||||
/// <returns> | |||||
/// A String of hexadecimal pairs separated by hyphens, where each pair | |||||
/// represents the corresponding element in value; for example, "7F-2C-4A". | |||||
/// </returns> | |||||
public static string ToString(byte[] value) | |||||
{ | |||||
return BitConverter.ToString(value); | |||||
} | |||||
/// <summary> | |||||
/// Returns a String converted from the elements of a byte array starting at a specified array position. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <remarks>The elements from array position startIndex to the end of the array are converted.</remarks> | |||||
/// <returns> | |||||
/// A String of hexadecimal pairs separated by hyphens, where each pair | |||||
/// represents the corresponding element in value; for example, "7F-2C-4A". | |||||
/// </returns> | |||||
public static string ToString(byte[] value, int startIndex) | |||||
{ | |||||
return BitConverter.ToString(value, startIndex); | |||||
} | |||||
/// <summary> | |||||
/// Returns a String converted from a specified number of bytes at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <param name="length">The number of bytes to convert.</param> | |||||
/// <remarks>The length elements from array position startIndex are converted.</remarks> | |||||
/// <returns> | |||||
/// A String of hexadecimal pairs separated by hyphens, where each pair | |||||
/// represents the corresponding element in value; for example, "7F-2C-4A". | |||||
/// </returns> | |||||
public static string ToString(byte[] value, int startIndex, int length) | |||||
{ | |||||
return BitConverter.ToString(value, startIndex, length); | |||||
} | |||||
#endregion | |||||
#region Decimal conversions | |||||
/// <summary> | |||||
/// Returns a decimal value converted from sixteen bytes | |||||
/// at a specified position in a byte array. | |||||
/// </summary> | |||||
/// <param name="value">An array of bytes.</param> | |||||
/// <param name="startIndex">The starting position within value.</param> | |||||
/// <returns>A decimal formed by sixteen bytes beginning at startIndex.</returns> | |||||
public decimal ToDecimal (byte[] value, int startIndex) | |||||
{ | |||||
// HACK: This always assumes four parts, each in their own endianness, | |||||
// starting with the first part at the start of the byte array. | |||||
// On the other hand, there's no real format specified... | |||||
int[] parts = new int[4]; | |||||
for (int i=0; i < 4; i++) | |||||
{ | |||||
parts[i] = ToInt32(value, startIndex+i*4); | |||||
} | |||||
return new Decimal(parts); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified decimal value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 16.</returns> | |||||
public byte[] GetBytes(decimal value) | |||||
{ | |||||
byte[] bytes = new byte[16]; | |||||
int[] parts = decimal.GetBits(value); | |||||
for (int i=0; i < 4; i++) | |||||
{ | |||||
CopyBytesImpl(parts[i], 4, bytes, i*4); | |||||
} | |||||
return bytes; | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified decimal value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">A character to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(decimal value, byte[] buffer, int index) | |||||
{ | |||||
int[] parts = decimal.GetBits(value); | |||||
for (int i=0; i < 4; i++) | |||||
{ | |||||
CopyBytesImpl(parts[i], 4, buffer, i*4+index); | |||||
} | |||||
} | |||||
#endregion | |||||
#region GetBytes conversions | |||||
/// <summary> | |||||
/// Returns an array with the given number of bytes formed | |||||
/// from the least significant bytes of the specified value. | |||||
/// This is used to implement the other GetBytes methods. | |||||
/// </summary> | |||||
/// <param name="value">The value to get bytes for</param> | |||||
/// <param name="bytes">The number of significant bytes to return</param> | |||||
byte[] GetBytes(long value, int bytes) | |||||
{ | |||||
byte[] buffer = new byte[bytes]; | |||||
CopyBytes(value, bytes, buffer, 0); | |||||
return buffer; | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified Boolean value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">A Boolean value.</param> | |||||
/// <returns>An array of bytes with length 1.</returns> | |||||
public byte[] GetBytes(bool value) | |||||
{ | |||||
return BitConverter.GetBytes(value); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified Unicode character value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">A character to convert.</param> | |||||
/// <returns>An array of bytes with length 2.</returns> | |||||
public byte[] GetBytes(char value) | |||||
{ | |||||
return GetBytes(value, 2); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified double-precision floating point value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 8.</returns> | |||||
public byte[] GetBytes(double value) | |||||
{ | |||||
return GetBytes(DoubleToInt64Bits(value), 8); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified 16-bit signed integer value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 2.</returns> | |||||
public byte[] GetBytes(short value) | |||||
{ | |||||
return GetBytes(value, 2); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified 32-bit signed integer value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 4.</returns> | |||||
public byte[] GetBytes(int value) | |||||
{ | |||||
return GetBytes(value, 4); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified 64-bit signed integer value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 8.</returns> | |||||
public byte[] GetBytes(long value) | |||||
{ | |||||
return GetBytes(value, 8); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified single-precision floating point value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 4.</returns> | |||||
public byte[] GetBytes(float value) | |||||
{ | |||||
return GetBytes(SingleToInt32Bits(value), 4); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified 16-bit unsigned integer value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 2.</returns> | |||||
public byte[] GetBytes(ushort value) | |||||
{ | |||||
return GetBytes(value, 2); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified 32-bit unsigned integer value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 4.</returns> | |||||
public byte[] GetBytes(uint value) | |||||
{ | |||||
return GetBytes(value, 4); | |||||
} | |||||
/// <summary> | |||||
/// Returns the specified 64-bit unsigned integer value as an array of bytes. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <returns>An array of bytes with length 8.</returns> | |||||
public byte[] GetBytes(ulong value) | |||||
{ | |||||
return GetBytes(unchecked((long)value), 8); | |||||
} | |||||
#endregion | |||||
#region CopyBytes conversions | |||||
/// <summary> | |||||
/// Copies the given number of bytes from the least-specific | |||||
/// end of the specified value into the specified byte array, beginning | |||||
/// at the specified index. | |||||
/// This is used to implement the other CopyBytes methods. | |||||
/// </summary> | |||||
/// <param name="value">The value to copy bytes for</param> | |||||
/// <param name="bytes">The number of significant bytes to copy</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
void CopyBytes(long value, int bytes, byte[] buffer, int index) | |||||
{ | |||||
if (buffer==null) | |||||
{ | |||||
throw new ArgumentNullException("buffer", "Byte array must not be null"); | |||||
} | |||||
if (buffer.Length < index+bytes) | |||||
{ | |||||
throw new ArgumentOutOfRangeException("Buffer not big enough for value"); | |||||
} | |||||
CopyBytesImpl(value, bytes, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the given number of bytes from the least-specific | |||||
/// end of the specified value into the specified byte array, beginning | |||||
/// at the specified index. | |||||
/// This must be implemented in concrete derived classes, but the implementation | |||||
/// may assume that the value will fit into the buffer. | |||||
/// </summary> | |||||
/// <param name="value">The value to copy bytes for</param> | |||||
/// <param name="bytes">The number of significant bytes to copy</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
protected abstract void CopyBytesImpl(long value, int bytes, byte[] buffer, int index); | |||||
/// <summary> | |||||
/// Copies the specified Boolean value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">A Boolean value.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(bool value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(value ? 1 : 0, 1, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified Unicode character value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">A character to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(char value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(value, 2, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified double-precision floating point value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(double value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(DoubleToInt64Bits(value), 8, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified 16-bit signed integer value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(short value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(value, 2, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified 32-bit signed integer value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(int value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(value, 4, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified 64-bit signed integer value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(long value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(value, 8, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified single-precision floating point value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(float value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(SingleToInt32Bits(value), 4, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified 16-bit unsigned integer value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(ushort value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(value, 2, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified 32-bit unsigned integer value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(uint value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(value, 4, buffer, index); | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified 64-bit unsigned integer value into the specified byte array, | |||||
/// beginning at the specified index. | |||||
/// </summary> | |||||
/// <param name="value">The number to convert.</param> | |||||
/// <param name="buffer">The byte array to copy the bytes into</param> | |||||
/// <param name="index">The first index into the array to copy the bytes into</param> | |||||
public void CopyBytes(ulong value, byte[] buffer, int index) | |||||
{ | |||||
CopyBytes(unchecked((long)value), 8, buffer, index); | |||||
} | |||||
#endregion | |||||
#region Private struct used for Single/Int32 conversions | |||||
/// <summary> | |||||
/// Union used solely for the equivalent of DoubleToInt64Bits and vice versa. | |||||
/// </summary> | |||||
[StructLayout(LayoutKind.Explicit)] | |||||
struct Int32SingleUnion | |||||
{ | |||||
/// <summary> | |||||
/// Int32 version of the value. | |||||
/// </summary> | |||||
[FieldOffset(0)] | |||||
int i; | |||||
/// <summary> | |||||
/// Single version of the value. | |||||
/// </summary> | |||||
[FieldOffset(0)] | |||||
float f; | |||||
/// <summary> | |||||
/// Creates an instance representing the given integer. | |||||
/// </summary> | |||||
/// <param name="i">The integer value of the new instance.</param> | |||||
internal Int32SingleUnion(int i) | |||||
{ | |||||
this.f = 0; // Just to keep the compiler happy | |||||
this.i = i; | |||||
} | |||||
/// <summary> | |||||
/// Creates an instance representing the given floating point number. | |||||
/// </summary> | |||||
/// <param name="f">The floating point value of the new instance.</param> | |||||
internal Int32SingleUnion(float f) | |||||
{ | |||||
this.i = 0; // Just to keep the compiler happy | |||||
this.f = f; | |||||
} | |||||
/// <summary> | |||||
/// Returns the value of the instance as an integer. | |||||
/// </summary> | |||||
internal int AsInt32 | |||||
{ | |||||
get { return i; } | |||||
} | |||||
/// <summary> | |||||
/// Returns the value of the instance as a floating point number. | |||||
/// </summary> | |||||
internal float AsSingle | |||||
{ | |||||
get { return f; } | |||||
} | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@ -0,0 +1,17 @@ | |||||
namespace MiscUtil.Conversion | |||||
{ | |||||
/// <summary> | |||||
/// Endianness of a converter | |||||
/// </summary> | |||||
public enum Endianness | |||||
{ | |||||
/// <summary> | |||||
/// Little endian - least significant byte first | |||||
/// </summary> | |||||
LittleEndian, | |||||
/// <summary> | |||||
/// Big endian - most significant byte first | |||||
/// </summary> | |||||
BigEndian | |||||
} | |||||
} |
@ -0,0 +1,66 @@ | |||||
| |||||
namespace MiscUtil.Conversion | |||||
{ | |||||
/// <summary> | |||||
/// Implementation of EndianBitConverter which converts to/from little-endian | |||||
/// byte arrays. | |||||
/// </summary> | |||||
public sealed class LittleEndianBitConverter : EndianBitConverter | |||||
{ | |||||
/// <summary> | |||||
/// Indicates the byte order ("endianess") in which data is converted using this class. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Different computer architectures store data using different byte orders. "Big-endian" | |||||
/// means the most significant byte is on the left end of a word. "Little-endian" means the | |||||
/// most significant byte is on the right end of a word. | |||||
/// </remarks> | |||||
/// <returns>true if this converter is little-endian, false otherwise.</returns> | |||||
public sealed override bool IsLittleEndian() | |||||
{ | |||||
return true; | |||||
} | |||||
/// <summary> | |||||
/// Indicates the byte order ("endianess") in which data is converted using this class. | |||||
/// </summary> | |||||
public sealed override Endianness Endianness | |||||
{ | |||||
get { return Endianness.LittleEndian; } | |||||
} | |||||
/// <summary> | |||||
/// Copies the specified number of bytes from value to buffer, starting at index. | |||||
/// </summary> | |||||
/// <param name="value">The value to copy</param> | |||||
/// <param name="bytes">The number of bytes to copy</param> | |||||
/// <param name="buffer">The buffer to copy the bytes into</param> | |||||
/// <param name="index">The index to start at</param> | |||||
protected override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) | |||||
{ | |||||
for (int i=0; i < bytes; i++) | |||||
{ | |||||
buffer[i+index] = unchecked((byte)(value&0xff)); | |||||
value = value >> 8; | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Returns a value built from the specified number of bytes from the given buffer, | |||||
/// starting at index. | |||||
/// </summary> | |||||
/// <param name="buffer">The data in byte array format</param> | |||||
/// <param name="startIndex">The first index to use</param> | |||||
/// <param name="bytesToConvert">The number of bytes to use</param> | |||||
/// <returns>The value built from the given bytes</returns> | |||||
protected override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) | |||||
{ | |||||
long ret = 0; | |||||
for (int i=0; i < bytesToConvert; i++) | |||||
{ | |||||
ret = unchecked((ret << 8) | buffer[startIndex+bytesToConvert-1-i]); | |||||
} | |||||
return ret; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
using GenProto; | |||||
phoneNumber phoneNumber = new phoneNumber() | |||||
{ | |||||
number = new test { aa = "hello" }, | |||||
type = 100 | |||||
}; | |||||
var data = phoneNumber.Serialize(); | |||||
var ss = new phoneNumber(); | |||||
ss.Deserialize(data); | |||||
Console.WriteLine("Hello, World!"); |
@ -1,4 +1,4 @@ | |||||
-module(test). | |||||
-module(protoTest). | |||||
-include("protoMsg.hrl"). | -include("protoMsg.hrl"). | ||||
-compile(export_all). | -compile(export_all). |
@ -1,6 +1,6 @@ | |||||
-module(test). | |||||
-module(protoTest). | |||||
-include("protoMsg.hrl"). | |||||
-include("../erl/protoMsg.hrl"). | |||||
-compile(export_all). | -compile(export_all). | ||||
encode_int32(N) -> | encode_int32(N) -> |
@ -1 +1 @@ | |||||
genProto ../proto ./ ./ | |||||
genProto ../proto erl ./erl ./erl lua ./lua cs ./cs |
@ -1 +1 @@ | |||||
genProto ../proto ./ ./ | |||||
genProto ../proto erl ./erl ./erl lua ./lua cs ./cs |
@ -1,2 +0,0 @@ | |||||
@echo off | |||||
escript.exe "%~dpn0" %* |
@ -0,0 +1,251 @@ | |||||
function ByteArray(endian) | |||||
local mRadix = {[8]="%03o", [10]="%03u", [16]="%02X"} -- 进制 | |||||
local mBuf = {} -- 二进制字节流 | |||||
local mPos = 1 -- 读写位置 | |||||
-- 验证读写位置 | |||||
local function checkAvailable() | |||||
assert(#mBuf >= mPos, string.format("End of file was encountered. pos: %d, length: %d.", mPos, #mBuf)) | |||||
end | |||||
-- 获取字符码 | |||||
local function getLetterCode(fmt) | |||||
fmt = fmt or "" | |||||
return ">"..fmt | |||||
end | |||||
-- 读单个字节 | |||||
local function readRawByte() | |||||
checkAvailable() | |||||
local rawByte = mBuf[mPos] | |||||
mPos = mPos + 1 | |||||
return rawByte | |||||
end | |||||
-- 写单个字节 | |||||
local function writeRawByte(rawByte) | |||||
if mPos > #mBuf + 1 then | |||||
for i=#mBuf + 1, mPos - 1 do | |||||
mBuf[i] = string.char(0) | |||||
end | |||||
end | |||||
mBuf[mPos] = rawByte | |||||
mPos = mPos + 1 | |||||
end | |||||
-- 读字节流 | |||||
local function readBuf(length) | |||||
checkAvailable() | |||||
local buf = table.concat(mBuf, "", mPos, mPos + length - 1) | |||||
mPos = mPos + length | |||||
return buf | |||||
end | |||||
-- 写字节流 | |||||
local function writeBuf(buf) | |||||
for i=1, #buf do | |||||
writeRawByte(buf:sub(i, i)) | |||||
end | |||||
end | |||||
-- 读字符串 | |||||
local function read_string_bytes(length) | |||||
if 0 == length then | |||||
return "" | |||||
end | |||||
local tmp, value = string.unpack(readBuf(length), getLetterCode("A"..length)) | |||||
return value | |||||
end | |||||
-- 写字符串 | |||||
local function write_string_bytes(value) | |||||
local buf = string.pack(getLetterCode("A"), value) | |||||
writeBuf(buf) | |||||
end | |||||
---------------------------------------------------------------------- | |||||
-- public method | |||||
---------------------------------------------------------------------- | |||||
local ba = {} | |||||
-- 设置字节流 | |||||
ba.setBytes = function(buf) | |||||
if #mBuf > 0 then | |||||
return | |||||
end | |||||
writeBuf(buf) | |||||
mPos = 1 -- 这里必须重置读写位置为1,方能保证接下去的读操作正确 | |||||
end | |||||
-- 获取字节流 | |||||
ba.getBytes = function() | |||||
local bytes = {} | |||||
for i=1, #mBuf do | |||||
bytes[#bytes+1] = string.byte(mBuf[i]) | |||||
end | |||||
local packRes = string.pack(getLetterCode("b"..#bytes), unpack(bytes)) | |||||
return packRes | |||||
end | |||||
-- 获取字节流长度 | |||||
ba.getLength = function() | |||||
return #mBuf | |||||
end | |||||
-- 字节流转为字符串,radix-8,10,16 | |||||
ba.toString = function(radix, separator) | |||||
radix = radix or 16 | |||||
radix = mRadix[radix] or "%02X" | |||||
separator = separator or " " | |||||
local bytes = {} | |||||
for i=1, #mBuf do | |||||
bytes[i] = string.format(radix..separator, string.byte(mBuf[i])) | |||||
end | |||||
return table.concat(bytes) | |||||
end | |||||
---------------------------------------------------------------------- | |||||
-- 读16位整型 | |||||
ba.read_int16 = function() | |||||
local tmp, value = string.unpack(readBuf(2), getLetterCode("h")) | |||||
return value | |||||
end | |||||
-- 写16位整型 | |||||
ba.write_int16 = function(value) | |||||
local buf = string.pack(getLetterCode("h"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读16位无符号整型 | |||||
ba.read_uint16 = function() | |||||
local tmp, value = string.unpack(readBuf(2), getLetterCode("H")) | |||||
return value | |||||
end | |||||
-- 写16位无符号整型 | |||||
ba.write_uint16 = function(value) | |||||
local sstr = getLetterCode("H") | |||||
local buf = string.pack(sstr, value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读32位整型 | |||||
ba.read_int32 = function() | |||||
local tmp, value = string.unpack(readBuf(4), getLetterCode("i")) | |||||
return value | |||||
end | |||||
-- 写32位整型 | |||||
ba.write_int32 = function(value) | |||||
local buf = string.pack(getLetterCode("i"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读32位无符号整型 | |||||
ba.read_uint32 = function() | |||||
local tmp, value = string.unpack(readBuf(4), getLetterCode("I")) | |||||
return value | |||||
end | |||||
-- 写32位无符号整型 | |||||
ba.write_uint32 = function(value) | |||||
local buf = string.pack(getLetterCode("I"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读长整型 | |||||
ba.read_long = function() | |||||
local tmp, value = string.unpack(readBuf(4), getLetterCode("l")) | |||||
return value | |||||
end | |||||
-- 写长整型 | |||||
ba.write_long = function(value) | |||||
local buf = string.pack(getLetterCode("l"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读无符号长整型 | |||||
ba.read_ulong = function() | |||||
local tmp, value = string.unpack(readBuf(4), getLetterCode("L")) | |||||
return value | |||||
end | |||||
-- 写无符号长整型 | |||||
ba.write_ulong = function(value) | |||||
local buf = string.pack(getLetterCode("L"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读64位整型 | |||||
ba.read_int64 = function() | |||||
-- local tmp, value = string.unpack(readBuf(8), getLetterCode("m")) | |||||
-- return value | |||||
return read_string_bytes(8) | |||||
end | |||||
-- 写64位整型 | |||||
ba.write_int64 = function(value) | |||||
-- local buf = string.pack(getLetterCode("m"), value) | |||||
-- writeBuf(buf) | |||||
local buf = string.pack(getLetterCode("A"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读64位无符号整型 | |||||
ba.read_uint64 = function() | |||||
-- local tmp, value = string.unpack(readBuf(8), getLetterCode("M")) | |||||
-- return value | |||||
return read_string_bytes(8) | |||||
end | |||||
-- 写64位无符号整型 | |||||
ba.write_uint64 = function(value) | |||||
-- local buf = string.pack(getLetterCode("M"), value) | |||||
-- writeBuf(buf) | |||||
local buf = string.pack(getLetterCode("A"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读单精度浮点型 | |||||
ba.read_float = function() | |||||
local tmp, value = string.unpack(readBuf(4), getLetterCode("f")) | |||||
return value | |||||
end | |||||
-- 写单精度浮点型 | |||||
ba.write_float = function(value) | |||||
local buf = string.pack(getLetterCode("f"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读双精度浮点型 | |||||
ba.read_double = function() | |||||
local tmp, value = string.unpack(readBuf(8), getLetterCode("d")) | |||||
return value | |||||
end | |||||
-- 写双精度浮点型 | |||||
ba.write_double = function(value) | |||||
local buf = string.pack(getLetterCode("d"), value) | |||||
writeBuf(buf) | |||||
end | |||||
-- 读布尔型 | |||||
ba.read_bool = function() | |||||
return 1 == read_char() | |||||
end | |||||
-- 写布尔型 | |||||
ba.write_bool = function(value) | |||||
if value then | |||||
ba.write_char(1) | |||||
else | |||||
ba.write_char(0) | |||||
end | |||||
end | |||||
-- 读字符型 | |||||
ba.read_int8 = function() | |||||
local tmp, value = string.unpack(readRawByte(), "c") | |||||
return value | |||||
end | |||||
-- 写字符型 | |||||
ba.write_int8 = function(value) | |||||
writeRawByte(string.pack("c", value)) | |||||
end | |||||
-- 读单字节 | |||||
ba.read_uint8 = function() | |||||
-- 方法1 | |||||
-- return string.byte(readRawByte()) | |||||
-- 方法2 | |||||
local tmp, value = string.unpack(readRawByte(), "b") | |||||
return value | |||||
end | |||||
-- 写单字节 | |||||
ba.write_uint8 = function(value) | |||||
-- 方法1 | |||||
-- writeRawByte(string.char(value)) | |||||
-- 方法2 | |||||
writeRawByte(string.pack("b", value)) | |||||
end | |||||
-- 读字符串 | |||||
ba.read_string = function() | |||||
local length = ba.read_uint16() | |||||
return read_string_bytes(length) | |||||
end | |||||
-- 写字符串 | |||||
ba.write_string = function(value) | |||||
local buf = string.pack(getLetterCode("A"), value) | |||||
ba.write_uint16(#buf) | |||||
writeBuf(buf) | |||||
end | |||||
---------------------------------------------------------------------- | |||||
return ba | |||||
end | |||||
@ -0,0 +1,19 @@ | |||||
function test() | |||||
-- 封包 | |||||
local msgTable = new phoneNumber() | |||||
msgTable.number = new test() | |||||
msgTable.type = 1 | |||||
local byteArray = ByteArray() | |||||
byteArray = msgTable.build(byteArray) | |||||
local body = byteArray.getBytes() | |||||
-- 包头(大小端转换后的包体长度) | |||||
local bodyLength = string.len(body) -- 包体长度 | |||||
local head = string.pack(">I4", bodyLength) -- 包头四个字节,这里要用"i" | |||||
-- 发送 | |||||
local nSend = dosend(head..body) | |||||
-- 解包 先读取消息id 根据消息id到protoNane.lua 获取函数名 | |||||
-- 再根据函数名到 _G表找到反序列化的函数 | |||||
end |
@ -1 +0,0 @@ | |||||
start werl.exe |