Skip to content

Commit f7581f8

Browse files
authored
Merge pull request #28 from Kermalis/modern-C#
2 parents d06dbb8 + 3c50ae1 commit f7581f8

18 files changed

+3172
-3411
lines changed

README.md

Lines changed: 91 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@
33
[![NuGet](https://img.shields.io/nuget/v/EndianBinaryIO.svg)](https://www.nuget.org/packages/EndianBinaryIO)
44
[![NuGet downloads](https://img.shields.io/nuget/dt/EndianBinaryIO)](https://www.nuget.org/packages/EndianBinaryIO)
55

6-
A C# library that can read and write primitives, enums, arrays, and strings to streams using specified endianness, string encoding, and boolean sizes.
7-
Objects can also be read from/written to streams via reflection and attributes.
6+
This .NET library provides a simple API to read/write bytes from/to streams and spans using user-specified endianness.
7+
By default, supported types include primitives, enums, arrays, strings, and some common .NET struct types.
8+
Objects can also be read/written from/to streams via reflection and attributes.
9+
The developer can use the API even if their target behavior or data is not directly supported by using the `IBinarySerializable` interface, inheritting from the reader/writer, or using the manual `Span<T>`/`ReadOnlySpan<T>` methods without streams.
10+
Performance is the focus when not using reflection; no allocations unless absolutely necessary!
811

9-
The `IBinarySerializable` interface allows an object to be read and written in a customizable fashion.
12+
The `IBinarySerializable` interface allows an object to be read and written in a customizable fashion during reflection.
1013
Also included are attributes that can make reading and writing objects less of a headache.
1114
For example, classes and structs in C# cannot have ignored members when marshalling, but **EndianBinaryIO** has a `BinaryIgnoreAttribute` that will ignore properties when reading and writing.
1215

13-
There is also an `EndianBitConverter` static class which resembles `System.BitConverter`. With it you can convert to/from data types using arrays rather than streams, all with specific endianness.
16+
The `EndianBinaryPrimitives` static class which resembles `System.Buffers.Binary.BinaryPrimitives` is an API that converts to/from data types using `Span<T>`/`ReadOnlySpan<T>` with specific endianness, rather than streams.
17+
18+
----
19+
## Changelog For v2.0.0.0
20+
Be sure to check the comment at https://github.com/Kermalis/EndianBinaryIO/pull/28!
1421

1522
----
1623
## 🚀 Usage:
@@ -23,47 +30,46 @@ Assume we have the following definitions:
2330
```cs
2431
enum ByteSizedEnum : byte
2532
{
26-
Val1 = 0x20,
27-
Val2 = 0x80
33+
Val1 = 0x20,
34+
Val2 = 0x80,
2835
}
2936
enum ShortSizedEnum : short
3037
{
31-
Val1 = 0x40,
32-
Val2 = 0x800
38+
Val1 = 0x40,
39+
Val2 = 0x800,
3340
}
3441

3542
class MyBasicObj
3643
{
37-
// Properties
38-
public ShortSizedEnum Type { get; set; }
39-
public short Version { get; set; }
40-
public DateTime Date { get; set; }
41-
42-
// Property that is ignored when reading and writing
43-
[BinaryIgnore(true)]
44-
public ByteSizedEnum DoNotReadOrWrite { get; set; }
45-
46-
// Arrays work as well
47-
[BinaryArrayFixedLength(16)]
48-
public uint[] ArrayWith16Elements { get; set; }
49-
50-
// Boolean that occupies 4 bytes instead of one
51-
[BinaryBooleanSize(BooleanSize.U32)]
52-
public bool Bool32 { get; set; }
53-
54-
// String encoded in ASCII
55-
// Reads chars until the stream encounters a '\0'
56-
// Writing will append a '\0' at the end of the string
57-
[BinaryEncoding("ASCII")]
58-
[BinaryStringNullTerminated(true)]
59-
public string NullTerminatedASCIIString { get; set; }
60-
61-
// String encoded in UTF16-LE that will only read/write 10 chars
62-
// The BinaryStringTrimNullTerminatorsAttribute will indicate that every char from the first \0 will be removed from the string. This attribute also works with char arrays
63-
[BinaryEncoding("UTF-16")]
64-
[BinaryStringFixedLength(10)]
65-
[BinaryStringTrimNullTerminators(true)]
66-
public string UTF16String { get; set; }
44+
// Properties
45+
public ShortSizedEnum Type { get; set; }
46+
public short Version { get; set; }
47+
public DateTime Date { get; set; }
48+
49+
// Property that is ignored when reading and writing
50+
[BinaryIgnore]
51+
public ByteSizedEnum DoNotReadOrWrite { get; set; }
52+
53+
// Arrays work as well
54+
[BinaryArrayFixedLength(16)]
55+
public uint[] ArrayWith16Elements { get; set; }
56+
57+
// Boolean that occupies 4 bytes instead of one
58+
[BinaryBooleanSize(BooleanSize.U32)]
59+
public bool Bool32 { get; set; }
60+
61+
// String encoded in ASCII
62+
// Reads chars until the stream encounters a '\0'
63+
// Writing will append a '\0' at the end of the string
64+
[BinaryASCII]
65+
[BinaryStringNullTerminated]
66+
public string NullTerminatedASCIIString { get; set; }
67+
68+
// String encoded in UTF16-LE that will only read/write 10 chars
69+
// The BinaryStringTrimNullTerminatorsAttribute will indicate that every char from the first \0 will be removed from the string. This attribute also works with char arrays
70+
[BinaryStringFixedLength(10)]
71+
[BinaryStringTrimNullTerminators]
72+
public string UTF16String { get; set; }
6773
}
6874
```
6975
And assume these are our input bytes (in little endian):
@@ -107,89 +113,93 @@ obj.Type = reader.ReadEnum<ShortSizedEnum>(); // Reads the enum type based on th
107113
obj.Version = reader.ReadInt16(); // Reads a 'short' (2 bytes)
108114
obj.Date = reader.ReadDateTime(); // Reads a 'DateTime' (8 bytes)
109115
110-
obj.ArrayWith16Elements = reader.ReadUInt32s(16); // Reads 16 'uint's (4 bytes each)
116+
obj.ArrayWith16Elements = new uint[16];
117+
reader.ReadUInt32s(obj.ArrayWith16Elements); // Reads 16 'uint's (4 bytes each)
111118
112-
obj.Bool32 = reader.ReadBoolean(); // Reads a 'bool' (4 bytes in this case, since the reader was initiated with a default of BooleanSize.U32, but there is an overload to pass in one)
119+
obj.Bool32 = reader.ReadBoolean(); // Reads a 'bool' (4 bytes in this case, since the reader's current bool state is BooleanSize.U32)
113120
114-
obj.NullTerminatedASCIIString = reader.ReadStringNullTerminated(Encoding.ASCII); // Reads ASCII chars until a '\0' is read, then returns a 'string'
115-
obj.UTF16String = reader.ReadString(10, true, Encoding.Unicode); // Reads 10 UTF16-LE chars as a 'string' with the '\0's removed
121+
reader.ASCII = true; // Set the reader's ASCII state to true
122+
obj.NullTerminatedASCIIString = reader.ReadString_NullTerminated(); // Reads ASCII chars until a '\0' is read, then returns a 'string'
123+
124+
reader.ASCII = false; // Set the reader's ASCII state to false (UTF16-LE)
125+
obj.UTF16String = reader.ReadString_Count_TrimNullTerminators(10); // Reads 10 UTF16-LE chars as a 'string' with the '\0's removed
116126
```
117127
### Reading Automatically (With Reflection):
118128
```cs
119129
var reader = new EndianBinaryReader(stream, endianness: Endianness.LittleEndian);
120130
var obj = reader.ReadObject<MyBasicObj>(); // Create a 'MyBasicObj' and read all properties in order, ignoring any with a 'BinaryIgnoreAttribute'
121-
// Other objects that are properties in this object will also be read in the same way recursively
131+
// Other objects that are properties in this object will also be read in the same way recursively
122132
```
123133

124134
### Writing Manually:
125135
```cs
126136
var obj = new MyBasicObj
127137
{
128-
Type = ShortSizedEnum.Val2,
129-
Version = 511,
130-
Date = new DateTime(1998, 12, 30),
138+
Type = ShortSizedEnum.Val2,
139+
Version = 511,
140+
Date = new DateTime(1998, 12, 30),
131141

132-
DoNotReadOrWrite = ByteSizedEnum.Val1,
142+
DoNotReadOrWrite = ByteSizedEnum.Val1,
133143

134-
ArrayWith16Elements = new uint[16]
135-
{
136-
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
137-
},
144+
ArrayWith16Elements = new uint[16]
145+
{
146+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
147+
},
138148

139-
Bool32 = false,
149+
Bool32 = false,
140150

141-
NullTerminatedASCIIString = "EndianBinaryIO",
142-
UTF16String = "Kermalis"
151+
NullTerminatedASCIIString = "EndianBinaryIO",
152+
UTF16String = "Kermalis",
143153
};
144154

145155
var writer = new EndianBinaryWriter(stream, endianness: Endianness.LittleEndian, booleanSize: BooleanSize.U32);
146-
writer.Write(obj.Type); // Writes the enum type based on the amount of bytes of the enum's underlying type (short/2 in this case)
147-
writer.Write(obj.Version); // Writes a 'short' (2 bytes)
148-
writer.Write(obj.Date); // Writes a 'DateTime' (8 bytes)
149-
writer.Write(obj.ArrayWith16Elements); // Writes 16 'uint's (4 bytes each)
150-
writer.Write(obj.Bool32); // Writes a 'bool' (4 bytes in this case, since the reader was initiated with a default of BooleanSize.U32, but there is an overload to pass in one)
151-
writer.Write(obj.NullTerminatedASCIIString, true, Encoding.ASCII); // Writes the chars in the 'string' as ASCII and appends a '\0' at the end
152-
writer.Write(obj.UTF16String, 10, Encoding.Unicode); // Writes 10 UTF16-LE chars as a 'string'. If the string has more than 10 chars, it is truncated; if it has less, it is padded with '\0'
156+
writer.WriteEnum(obj.Type); // Writes the enum type based on the amount of bytes of the enum's underlying type (short/2 in this case)
157+
writer.WriteInt16(obj.Version); // Writes a 'short' (2 bytes)
158+
writer.WriteDateTime(obj.Date); // Writes a 'DateTime' (8 bytes)
159+
writer.WriteUInt32s(obj.ArrayWith16Elements); // Writes 16 'uint's (4 bytes each)
160+
writer.WriteBoolean(obj.Bool32); // Writes a 'bool' (4 bytes in this case, since the reader's current bool state is BooleanSize.U32)
161+
162+
writer.ASCII = true; // Set the reader's ASCII state to true
163+
writer.WriteChars_NullTerminated(obj.NullTerminatedASCIIString); // Writes the chars in the 'string' as ASCII and appends a '\0' at the end
164+
165+
writer.ASCII = false; // Set the reader's ASCII state to false (UTF16-LE)
166+
writer.WriteChars_Count(obj.UTF16String, 10); // Writes 10 UTF16-LE chars as a 'string'. If the string has more than 10 chars, it is truncated; if it has less, it is padded with '\0'
153167
```
154168
### Writing Automatically (With Reflection):
155169
```cs
156170
var obj = new MyBasicObj
157171
{
158-
Type = ShortSizedEnum.Val2,
159-
Version = 511,
160-
Date = new DateTime(1998, 12, 30),
172+
Type = ShortSizedEnum.Val2,
173+
Version = 511,
174+
Date = new DateTime(1998, 12, 30),
161175

162-
DoNotReadOrWrite = ByteSizedEnum.Val1,
176+
DoNotReadOrWrite = ByteSizedEnum.Val1,
163177

164-
ArrayWith16Elements = new uint[16]
165-
{
166-
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
167-
},
178+
ArrayWith16Elements = new uint[16]
179+
{
180+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
181+
},
168182

169-
Bool32 = false,
183+
Bool32 = false,
170184

171-
NullTerminatedASCIIString = "EndianBinaryIO",
172-
UTF16String = "Kermalis"
185+
NullTerminatedASCIIString = "EndianBinaryIO",
186+
UTF16String = "Kermalis",
173187
};
174188

175189
var writer = new EndianBinaryWriter(stream, endianness: Endianness.LittleEndian);
176190
writer.Write(obj); // Write all properties in the 'MyBasicObj' in order, ignoring any with a 'BinaryIgnoreAttribute'
177-
// Other objects that are properties in this object will also be written in the same way recursively
191+
// Other objects that are properties in this object will also be written in the same way recursively
178192
```
179193

180-
### EndianBitConverter Example:
194+
### EndianBinaryPrimitives Example:
181195
```cs
182-
byte[] bytes = new byte[] { 0xFF, 0x00, 0x00, 0x00 };
183-
uint value = (uint)EndianBitConverter.BytesToInt32(bytes, 0, Endianness.LittleEndian); // Will return (int)255
196+
byte[] bytes = new byte[] { 0xFF, 0x00, 0x00, 0x00, 0xBB, 0xEE, 0xEE, 0xFF };
197+
uint value = EndianBinaryPrimitives.ReadUInt32(bytes, Endianness.LittleEndian); // Will return 255
184198
185199
value = 128;
186-
bytes = EndianBitConverter.Int32ToBytes((int)value, Endianness.LittleEndian); // Will return (byte[]){ 0x80, 0x00, 0x00, 0x00 }
200+
EndianBinaryPrimitives.WriteUInt32(bytes.AsSpan(4, 4), value, Endianness.LittleEndian); // bytes is now { 0xFF, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 }
187201
```
188202

189-
----
190-
## To Do:
191-
* Documentation
192-
193203
----
194204
## EndianBinaryIOTests Uses:
195205
* [xUnit.net](https://github.com/xunit/xunit)

0 commit comments

Comments
 (0)