Skip to content

Additional Features

Eric Freed edited this page Aug 26, 2021 · 2 revisions

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.

VarInt and VarLong

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.

ZLib Streams

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.

Extension Methods

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.

Clone this wiki locally