-
Notifications
You must be signed in to change notification settings - Fork 9
Additional Features
While the design of the SharpNBT abstracts these features away where they are never required to be used directly by consumers, they have been made public and included in the API, as they are commonly needed for related projects that SharpNBT may already be used as a component of.
Static VarInt
and VarLong
classes with a maximum size up to 5 and 10 bytes respectfully, analogous to signed 32-bit and 64-bit integers. The classes provide various methods for reading/writing each to either buffers or streams, and optionally support Zig-zag encoding if needed.
The inclusion of a ZLibStream
class, with an Adler32
checksum implementation, capable of reading and writing in ZLib format with headers and post-fixed CRC checksum.
The addition of a SwapEndian
extension method added to short
, ushort
, int
, uint
, long
, ulong
, float
, and double
. Uses optimized bit-shifting in groups to limit the number of CPU instructions, and avoids the use of the significantly slower BitConverter.GetBytes
and Array.Reverse
combination. That method, while easy and quick to get working, is very slow in comparison.
It is difficult to even give an accurate benchmark, as on my machine the methods for bit-shifting a 64-bit unsigned integer cannot even register, they are equal to calling an "empty method", with repeatable results.
Optimized Bit-Shifting Used by SharpNBT
public ulong SwapEndian(ulong value)
{
value = ((value << 8) & 0xFF00FF00FF00FF00UL) | ((value >> 8) & 0x00FF00FF00FF00FFUL);
value = ((value << 16) & 0xFFFF0000FFFF0000UL) | ((value >> 16) & 0x0000FFFF0000FFFFUL);
return (value << 32) | (value >> 32);
}
Typical "Array.Reverse" Method
public ulong SwapEndian(ulong value)
{
var bytes = BitConverter.GetBytes(value);
Array.Reverse(bytes);
return BitConverter.ToUInt64(bytes, 0);
}
Benchmark Results
BenchmarkDotNet=v0.13.1, OS=arch
Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=5.0.205
[Host] : .NET 5.0.8 (5.0.821.37701), X64 RyuJIT
DefaultJob : .NET 5.0.8 (5.0.821.37701), X64 RyuJIT
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Allocated |
|-------------- |-----------:|----------:|----------:|------:|--------:|-------:|----------:|
| 'Bit Shift' | 0.0000 ns | 0.0000 ns | 0.0000 ns | ? | ? | - | - |
| Array.Reverse | 15.5293 ns | 0.0919 ns | 0.0718 ns | ? | ? | 0.0102 | 32 B |
// * Warnings *
ZeroMeasurement
ByteSwapping.'Bit Shift': Default -> The method duration is indistinguishable from the empty method duration
BaselineCustomAnalyzer
Summary -> A question mark '?' symbol indicates that it was not possible to compute the (Ratio, RatioSD) column(s) because the baseline value is too close to zero.
If your eyes are glazing over, it is essentially saying that the bit-shifting method is indistinguishable from a no-op and/or empty method call that does nothing. Difficult to optimize it beyond that.
TL;DR: This is likely the most performant way it can be done in a safe context with the C# language and target runtime.