# Bitpacking

{% hint style="warning" %}
**Obsolete**: please use NetworkReader/Writer instead.&#x20;

Bitpacking won't be necessary with the new delta compression.&#x20;

NetworkReader/Writer are faster too.
{% endhint %}

**DOTSNET** uses **Bitpacking** via `BitWriter` to serialize, and `BitReader` to deserialize data down to the bit.

## Usage

For example, this is how we serialize an `int` and a `float3` position in a message:

```csharp
using DOTSNET;

public struct TestMessage : NetworkMessage
{
    public int classIndex;
    public float3 position;
    
    public byte GetID() => 0x31;

    public bool Serialize(ref BitWriter writer) =>
        writer.WriteInt(classIndex) &&
        writer.WriteFloat3(position);

    public bool Deserialize(ref BitReader reader) =>
        reader.ReadInt(out classIndex) &&
        reader.ReadFloat3(out position);
}
```

Reading and Writing is:

* **Atomic**: `WriteInt` either writes all 4 bytes `int`, or none if there is not enough space. `ReadInt` either reads all 4 bytes `int`, or none if the buffer doesn’t have enough data.
* **Allocation Free**: all reads and writes are allocation free for maximum performance.
* **Fast**: the Bitpacker’s internal buffer writes are always word aligned. This makes unaligned writes (`byte`/`ushort`/etc.) as fast as aligned writes (`uint`, `float`, etc.).

## Reference Passing

To avoid allocations, BitReader/Writer are value types (structs). When passing a reader/writer through functions, make sure to pass it as reference:

```csharp
public bool Serialize(ref BitWriter writer) =>
    writer.WriteInt(level);

public bool Deserialize(ref BitReader reader) =>
    reader.ReadInt(out level);

```

{% hint style="warning" %}
If you don’t pass it as reference, then it will create a copy of the Writer/Reader, which would not modify the original writer/reader’s Position.
{% endhint %}

In other words, **always pass** BitReader/Writer as **reference**!<br>

## Bit Compression

BitWriter/Reader default functions always write the full type unless **bitpacking** parameters are specified. For example, let’s serialize/deserialize a player’s **level** which requires **32 bits** `uint` by default:

```csharp
writer.WriteUInt(5);
reader.ReadUInt(out uint value);
```

If we know the value’s minimum and maximum data range, then we can do **heavy bandwidth optimizations** by packing it into the minimum amount of bits needed for that range. Our player’s **level** is always in the range of `[1..60]` so we can Bitpack it down from **32 bits** to **6 bits**:

```csharp
// a player level of '5' with an expected range of [1..60]
// => 60 possible values in that range
// => they all fit into 6 bits
// => so the value is encoded into 6 bits, instead of 32 bits uint!
writer.WriteUInt(5, 1, 60);
reader.ReadUInt(out uint value, 1, 60);
```

**DOTSNET**’s Bitpacking automatically does all the complicated bit math internally. All we need to do is specify the data type ranges if we know them.

This also works for `float`/`double` where we specify min, max, precision

```csharp
writer.WriteFloat(1.23f, -10f, 10f, 0.1f);
reader.ReadFloat(out float value);
```

Another interesting example is `bool`, which C# stores into **8 bit** = 1 byte by default. A `bool` is always either 1 or 0, which fits perfectly into just **1 bit**. DOTSNET packs bools automatically!

```csharp
writer.WriteBool(true);
reader.ReadBool(out bool value);
```

For a deeper understanding about **Bitpacking**, please read the excellent [article](https://gafferongames.com/post/reading_and_writing_packets/) by *Glenn Fiedler*!

## Burst

**DOTSNET** BitReader/Writers are burstable.&#x20;

Additionally, **DOTSNET** provides fixed size Readers/Writers for use in `IComponentData`:

* BitReader**128**
* BitWriter**128**

## Extending BitReader/Writer

You can extend BitReader/Writer with C#’s extension system:

```csharp
public struct MyStruct
{
    public int level;
    public int experience;
}
```

```csharp
using DOTSNET;    

public static class Extensions
{
    public static bool WriteMyStruct(ref this BitWriter writer, MyStruct value)
    {
        return writer.WriteInt(value.level) &&
               writer.WriteInt(value.experience);
    }
   
    public static bool ReadMyStruct(ref this SBitReader reader, out MyStruct value)
    {
        value = new MyStruct();
        return reader.ReadInt(out value.level) &&
               reader.ReadInt(out value.experience);
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dotsnet.gitbook.io/docs/user-manual/bitpacking.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
