Skip to content

Commit ba741fc

Browse files
committed
Int128 and UInt128
1 parent d3c3359 commit ba741fc

10 files changed

+317
-17
lines changed

Source/EndianBinaryIO.csproj

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net7.0</TargetFramework>
55
<LangVersion>latest</LangVersion>
66
<OutputType>Library</OutputType>
77
<RootNamespace>Kermalis.EndianBinaryIO</RootNamespace>
@@ -13,7 +13,7 @@
1313
<Title>EndianBinaryIO</Title>
1414
<PackageId>EndianBinaryIO</PackageId>
1515
<AssemblyName>EndianBinaryIO</AssemblyName>
16-
<Version>2.0.1</Version>
16+
<Version>2.1.0</Version>
1717
<RepositoryUrl>https://github.com/Kermalis/EndianBinaryIO</RepositoryUrl>
1818
<RepositoryType>git</RepositoryType>
1919
<Description>This .NET library provides a simple API to read/write bytes from/to streams and spans using user-specified endianness.
@@ -28,13 +28,10 @@ Project URL and Samples ― https://github.com/Kermalis/EndianBinaryIO</Descript
2828
<PackageTags>Serialization;Reflection;Endianness;LittleEndian;BigEndian;EndianBinaryIO</PackageTags>
2929
<PackageReadmeFile>README.md</PackageReadmeFile>
3030
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
31-
<PackageReleaseNotes>Version 2.0.1 Changelog:
32-
* Added TrimNullTerminators(ref char[] chars) and TrimNullTerminators(ref Span&lt;char&gt; chars) to EndianBinaryPrimitives which will remove all '\0' from the end
33-
* Added ReadSBytes(ReadOnlySpan&lt;byte&gt; src, Span&lt;sbyte&gt; dest) and WriteSBytes(Span&lt;byte&gt; dest, ReadOnlySpan&lt;sbyte&gt; src) to EndianBinaryPrimitives
34-
* Added heavily optimized enum methods to EndianBinaryPrimitives that use the same optimization techniques as the ones in EndianBinaryReader and EndianBinaryWriter
35-
* Added PeekBytes(Span&lt;byte&gt; dest) to EndianBinaryReader
31+
<PackageReleaseNotes>Version 2.1.0 Changelog:
32+
* TODO
3633

37-
No breaking changes from v2.0.0</PackageReleaseNotes>
34+
No breaking changes from v2.0.1</PackageReleaseNotes>
3835
</PropertyGroup>
3936

4037
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

Source/EndianBinaryPrimitives.cs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,24 @@ public static void TrimNullTerminators(ref ReadOnlySpan<char> chars)
4949
}
5050
}
5151

52+
// Why are these internal in BinaryPrimitives?
53+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
54+
public static Int128 ReverseEndianness(Int128 value)
55+
{
56+
return new Int128(
57+
BinaryPrimitives.ReverseEndianness((ulong)value), // _lower
58+
BinaryPrimitives.ReverseEndianness((ulong)(value >> 64)) // _upper
59+
);
60+
}
61+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
62+
public static UInt128 ReverseEndianness(UInt128 value)
63+
{
64+
return new UInt128(
65+
BinaryPrimitives.ReverseEndianness((ulong)value), // _lower
66+
BinaryPrimitives.ReverseEndianness((ulong)(value >> 64)) // _upper
67+
);
68+
}
69+
5270
#region Read
5371

5472
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -141,6 +159,52 @@ public static void ReadUInt64s(ReadOnlySpan<byte> src, Span<ulong> dest, Endiann
141159
}
142160
}
143161

162+
public static Int128 ReadInt128(ReadOnlySpan<byte> src, Endianness endianness)
163+
{
164+
if (src.Length < 16)
165+
{
166+
throw new ArgumentOutOfRangeException(nameof(src), null, "Length was less than 16");
167+
}
168+
169+
Int128 ret = Unsafe.ReadUnaligned<Int128>(ref MemoryMarshal.GetReference(src));
170+
if (endianness != SystemEndianness)
171+
{
172+
//ret = BinaryPrimitives.ReverseEndianness(ret);
173+
ret = ReverseEndianness(ret);
174+
}
175+
return ret;
176+
}
177+
public static void ReadInt128s(ReadOnlySpan<byte> src, Span<Int128> dest, Endianness endianness)
178+
{
179+
for (int i = 0; i < dest.Length; i++)
180+
{
181+
dest[i] = ReadInt128(src.Slice(i * 16, 16), endianness);
182+
}
183+
}
184+
185+
public static UInt128 ReadUInt128(ReadOnlySpan<byte> src, Endianness endianness)
186+
{
187+
if (src.Length < 16)
188+
{
189+
throw new ArgumentOutOfRangeException(nameof(src), null, "Length was less than 16");
190+
}
191+
192+
UInt128 ret = Unsafe.ReadUnaligned<UInt128>(ref MemoryMarshal.GetReference(src));
193+
if (endianness != SystemEndianness)
194+
{
195+
//ret = BinaryPrimitives.ReverseEndianness(ret);
196+
ret = ReverseEndianness(ret);
197+
}
198+
return ret;
199+
}
200+
public static void ReadUInt128s(ReadOnlySpan<byte> src, Span<UInt128> dest, Endianness endianness)
201+
{
202+
for (int i = 0; i < dest.Length; i++)
203+
{
204+
dest[i] = ReadUInt128(src.Slice(i * 16, 16), endianness);
205+
}
206+
}
207+
144208
public static Half ReadHalf(ReadOnlySpan<byte> src, Endianness endianness)
145209
{
146210
return endianness == Endianness.LittleEndian
@@ -548,6 +612,50 @@ public static void WriteUInt64s(Span<byte> dest, ReadOnlySpan<ulong> src, Endian
548612
}
549613
}
550614

615+
public static void WriteInt128(Span<byte> dest, Int128 value, Endianness endianness)
616+
{
617+
if (dest.Length < 16)
618+
{
619+
throw new ArgumentOutOfRangeException(nameof(dest), null, "Length was less than 16");
620+
}
621+
622+
if (endianness != SystemEndianness)
623+
{
624+
//value = BinaryPrimitives.ReverseEndianness(value);
625+
value = ReverseEndianness(value);
626+
}
627+
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(dest), value);
628+
}
629+
public static void WriteInt128s(Span<byte> dest, ReadOnlySpan<Int128> src, Endianness endianness)
630+
{
631+
for (int i = 0; i < src.Length; i++)
632+
{
633+
WriteInt128(dest.Slice(i * 16, 16), src[i], endianness);
634+
}
635+
}
636+
637+
public static void WriteUInt128(Span<byte> dest, UInt128 value, Endianness endianness)
638+
{
639+
if (dest.Length < 16)
640+
{
641+
throw new ArgumentOutOfRangeException(nameof(dest), null, "Length was less than 16");
642+
}
643+
644+
if (endianness != SystemEndianness)
645+
{
646+
//value = BinaryPrimitives.ReverseEndianness(value);
647+
value = ReverseEndianness(value);
648+
}
649+
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(dest), value);
650+
}
651+
public static void WriteUInt128s(Span<byte> dest, ReadOnlySpan<UInt128> src, Endianness endianness)
652+
{
653+
for (int i = 0; i < src.Length; i++)
654+
{
655+
WriteUInt128(dest.Slice(i * 16, 16), src[i], endianness);
656+
}
657+
}
658+
551659
public static void WriteHalf(Span<byte> dest, Half value, Endianness endianness)
552660
{
553661
if (endianness == Endianness.LittleEndian)

Source/EndianBinaryReader.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,26 @@ public void ReadUInt64s(Span<ulong> dest)
196196
{
197197
ReadArray(dest, 8, EndianBinaryPrimitives.ReadUInt64s);
198198
}
199+
public Int128 ReadInt128()
200+
{
201+
Span<byte> buffer = _buffer.AsSpan(0, 16);
202+
ReadBytes(buffer);
203+
return EndianBinaryPrimitives.ReadInt128(buffer, Endianness);
204+
}
205+
public void ReadInt128s(Span<Int128> dest)
206+
{
207+
ReadArray(dest, 16, EndianBinaryPrimitives.ReadInt128s);
208+
}
209+
public UInt128 ReadUInt128()
210+
{
211+
Span<byte> buffer = _buffer.AsSpan(0, 16);
212+
ReadBytes(buffer);
213+
return EndianBinaryPrimitives.ReadUInt128(buffer, Endianness);
214+
}
215+
public void ReadUInt128s(Span<UInt128> dest)
216+
{
217+
ReadArray(dest, 16, EndianBinaryPrimitives.ReadUInt128s);
218+
}
199219

200220
public Half ReadHalf()
201221
{

Source/EndianBinaryReader_Reflection.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ private bool TryReadSupportedObject(Type objType, [NotNullWhen(true)] out object
6161
case Type t when t == typeof(uint): { obj = ReadUInt32(); return true; }
6262
case Type t when t == typeof(long): { obj = ReadInt64(); return true; }
6363
case Type t when t == typeof(ulong): { obj = ReadUInt64(); return true; }
64+
case Type t when t == typeof(Int128): { obj = ReadInt128(); return true; }
65+
case Type t when t == typeof(UInt128): { obj = ReadUInt128(); return true; }
6466
case Type t when t == typeof(Half): { obj = ReadHalf(); return true; }
6567
case Type t when t == typeof(float): { obj = ReadSingle(); return true; }
6668
case Type t when t == typeof(double): { obj = ReadDouble(); return true; }
@@ -194,6 +196,18 @@ private object ReadPropertyValue_Array(object obj, Type objType, PropertyInfo pr
194196
ReadUInt64s(value);
195197
return value;
196198
}
199+
case Type t when t == typeof(Int128):
200+
{
201+
var value = new Int128[arrayLength];
202+
ReadInt128s(value);
203+
return value;
204+
}
205+
case Type t when t == typeof(UInt128):
206+
{
207+
var value = new UInt128[arrayLength];
208+
ReadUInt128s(value);
209+
return value;
210+
}
197211
case Type t when t == typeof(Half):
198212
{
199213
var value = new Half[arrayLength];

Source/EndianBinaryWriter.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,26 @@ public void WriteUInt64s(ReadOnlySpan<ulong> values)
212212
{
213213
WriteArray(values, 8, EndianBinaryPrimitives.WriteUInt64s);
214214
}
215+
public void WriteInt128(Int128 value)
216+
{
217+
Span<byte> buffer = _buffer.AsSpan(0, 16);
218+
EndianBinaryPrimitives.WriteInt128(buffer, value, Endianness);
219+
Stream.Write(buffer);
220+
}
221+
public void WriteInt128s(ReadOnlySpan<Int128> values)
222+
{
223+
WriteArray(values, 16, EndianBinaryPrimitives.WriteInt128s);
224+
}
225+
public void WriteUInt128(UInt128 value)
226+
{
227+
Span<byte> buffer = _buffer.AsSpan(0, 16);
228+
EndianBinaryPrimitives.WriteUInt128(buffer, value, Endianness);
229+
Stream.Write(buffer);
230+
}
231+
public void WriteUInt128s(ReadOnlySpan<UInt128> values)
232+
{
233+
WriteArray(values, 16, EndianBinaryPrimitives.WriteUInt128s);
234+
}
215235

216236
public void WriteHalf(Half value)
217237
{

Source/EndianBinaryWriter_Reflection.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ private bool TryWriteSupportedObject_NonArray(object obj)
6868
case uint v: WriteUInt32(v); return true;
6969
case long v: WriteInt64(v); return true;
7070
case ulong v: WriteUInt64(v); return true;
71+
case Int128 v: WriteInt128(v); return true;
72+
case UInt128 v: WriteUInt128(v); return true;
7173
case Half v: WriteHalf(v); return true;
7274
case float v: WriteSingle(v); return true;
7375
case double v: WriteDouble(v); return true;
@@ -110,6 +112,8 @@ private bool TryWriteSupportedObject_Array(Array obj, int length)
110112
case uint[] v: WriteUInt32s(v.AsSpan(0, length)); return true;
111113
case long[] v: WriteInt64s(v.AsSpan(0, length)); return true;
112114
case ulong[] v: WriteUInt64s(v.AsSpan(0, length)); return true;
115+
case Int128[] v: WriteInt128s(v.AsSpan(0, length)); return true;
116+
case UInt128[] v: WriteUInt128s(v.AsSpan(0, length)); return true;
113117
case Half[] v: WriteHalves(v.AsSpan(0, length)); return true;
114118
case float[] v: WriteSingles(v.AsSpan(0, length)); return true;
115119
case double[] v: WriteDoubles(v.AsSpan(0, length)); return true;

Testing/BasicTests.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Kermalis.EndianBinaryIO;
22
using System;
3+
using System.Globalization;
34
using System.IO;
45
using System.Linq;
56
using Xunit;
@@ -14,6 +15,7 @@ private sealed class MyBasicObj
1415
public ShortSizedEnum Type { get; set; }
1516
public short Version { get; set; }
1617
public DateTime Date { get; set; }
18+
public Int128 Int128 { get; set; }
1719

1820
// Property that is ignored when reading and writing
1921
[BinaryIgnore]
@@ -42,11 +44,14 @@ private sealed class MyBasicObj
4244

4345
#region Constants
4446

47+
private static readonly DateTime _expectedDateTime = new(1998, 12, 30);
48+
private static readonly Int128 _expectedInt128 = Int128.Parse("48,045,707,429,126,174,655,160,174,263,614,327,112", NumberStyles.AllowThousands);
4549
private static readonly byte[] _bytes = new byte[]
4650
{
4751
0x00, 0x08, // ShortSizedEnum.Val2
4852
0xFF, 0x01, // (short)511
4953
0x00, 0x00, 0x4A, 0x7A, 0x9E, 0x01, 0xC0, 0x08, // (DateTime)Dec. 30, 1998
54+
0x48, 0x49, 0x80, 0x44, 0x82, 0x44, 0x88, 0xC0, 0x42, 0x24, 0x88, 0x12, 0x44, 0x44, 0x25, 0x24, // (Int128)48,045,707,429,126,174,655,160,174,263,614,327,112
5055

5156
0x00, 0x00, 0x00, 0x00, // (uint)0
5257
0x01, 0x00, 0x00, 0x00, // (uint)1
@@ -77,7 +82,8 @@ private static MyBasicObj GetObj()
7782
{
7883
Type = ShortSizedEnum.Val2,
7984
Version = 511,
80-
Date = new DateTime(1998, 12, 30),
85+
Date = _expectedDateTime,
86+
Int128 = _expectedInt128,
8187

8288
DoNotReadOrWrite = ByteSizedEnum.Val1,
8389

@@ -106,7 +112,8 @@ public void ReadObject()
106112

107113
Assert.Equal(ShortSizedEnum.Val2, obj.Type); // Enum works
108114
Assert.Equal(511, obj.Version); // short works
109-
Assert.Equal(new DateTime(1998, 12, 30), obj.Date); // DateTime works
115+
Assert.Equal(_expectedDateTime, obj.Date); // DateTime works
116+
Assert.Equal(_expectedInt128, obj.Int128); // Int128 works
110117

111118
Assert.Equal(default, obj.DoNotReadOrWrite); // Ignored
112119

@@ -135,7 +142,9 @@ public void ReadManually()
135142
obj.Version = reader.ReadInt16();
136143
Assert.Equal(511, obj.Version); // short works
137144
obj.Date = reader.ReadDateTime();
138-
Assert.Equal(new DateTime(1998, 12, 30), obj.Date); // DateTime works
145+
Assert.Equal(_expectedDateTime, obj.Date); // DateTime works
146+
obj.Int128 = reader.ReadInt128();
147+
Assert.Equal(_expectedInt128, obj.Int128); // Int128 works
139148

140149
obj.ArrayWith16Elements = new uint[16];
141150
reader.ReadUInt32s(obj.ArrayWith16Elements);
@@ -181,7 +190,10 @@ public void WriteManually()
181190
writer.WriteEnum(obj.Type);
182191
writer.WriteInt16(obj.Version);
183192
writer.WriteDateTime(obj.Date);
193+
writer.WriteInt128(obj.Int128);
194+
184195
writer.WriteUInt32s(obj.ArrayWith16Elements);
196+
185197
writer.WriteBoolean(obj.Bool32);
186198

187199
writer.ASCII = true;

Testing/EndianBinaryTesting.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net7.0</TargetFramework>
55
<LangVersion>latest</LangVersion>
66
<RootNamespace>Kermalis.EndianBinaryIOTests</RootNamespace>
77
<IsPackable>false</IsPackable>
@@ -13,7 +13,7 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
16+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
1717
<PackageReference Include="xunit" Version="2.4.2" />
1818
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
1919
<PrivateAssets>all</PrivateAssets>

Testing/FloatTests.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ public sealed class FloatTests
99
{
1010
#region Constants
1111

12-
private const float TEST_VAL_SINGLE = 1234.1234f;
13-
private const double TEST_VAL_DOUBLE = 12345678.12345678d;
14-
private const decimal TEST_VAL_DECIMAL = 12345678909876543210.123456789m;
15-
12+
private const float TEST_VAL_SINGLE = 1_234.1234f;
1613
private static readonly byte[] _bigEndianBytes_Single = new byte[4]
1714
{
1815
0x44, 0x9A, 0x43, 0xF3,
@@ -21,6 +18,8 @@ public sealed class FloatTests
2118
{
2219
0xF3, 0x43, 0x9A, 0x44,
2320
};
21+
22+
private const double TEST_VAL_DOUBLE = 12_345_678.12345678d;
2423
private static readonly byte[] _bigEndianBytes_Double = new byte[8]
2524
{
2625
0x41, 0x67, 0x8C, 0x29, 0xC3, 0xF3, 0x5B, 0xA2,
@@ -29,6 +28,8 @@ public sealed class FloatTests
2928
{
3029
0xA2, 0x5B, 0xF3, 0xC3, 0x29, 0x8C, 0x67, 0x41,
3130
};
31+
32+
private const decimal TEST_VAL_DECIMAL = 12_345_678_909_876_543_210.123456789m;
3233
private static readonly byte[] _bigEndianBytes_Decimal = new byte[16]
3334
{
3435
0xA0, 0x84, 0x71, 0x15,

0 commit comments

Comments
 (0)