Rakkarsoft LLC

Bitstream Overview
Description

The bitstream class is a helper class under the namespace RakNet that is used to wrap a dynamic array for the purpose of packing and unpacking bits. Its main three benefits are:
  1. Creating packets dynamically
  2. Compression
  3. Writing Bits
With structs you have to predefine your structures and cast them to a (char*). With a bitstream, you can choose to write blocks at runtime, depending on the context. Bitstreams can compress the native types.

The compression is quite simple and uses the following algorithm:
1. Is the upper half all 0's (1's for unsigned types)?
TRUE - Write a 1
FALSE - Write a 0, and the upper half
2. Repeat with the lower half, until we are at half a byte.
What this means is you will save bits if your data under half its maximum range. So if you know this is usually the case in advance, you can use WriteCompressed instead of Write and ReadCompressed instead of read.

Finally, you can write bits. Most of the time you will not care about this. However, when writing booleans it will automatically only write one bit. This can also be very useful for encryption since your data will no longer be byte aligned.

Writing Data

Writing data is a snap. Create a bitstream, and call the Write method for each of your data types. If your data type is a native type, then the correct overloaded version will be called. If the data type is your own type, you must cast it first as in

bitStream.Write((char*)&myType, sizeof(myType);

Optional - One of the constructor versions takes a length in bytes as a parameter. If you have an idea of the size of your data you can pass this number when creating the bitstream to avoid internal reallocations.

See Creating Packets for more details.

Reading Data

Reading data is equally simple. Create a bitstream, and in the constructor assign it your data.

// Assuming we have a Packet *
BitStream myBitStream(packet->data, packet->length, false);

See Receiving Packets for a more complete example.

Major functions
See BitStream.h for a full list of functions.

Constructor
BitStream(int initialBytesToAllocate);
Use the constuctor version that takes an int to determine how many bytes to initially allocate. While not necessary, you can avoid reallocations when doing writes this way.

Constructor
BitStream(const char* _data, unsigned int lengthInBytes, bool _copyData);
This version of the constructor gives the bitstream some initial data. This is used for interpreting an existing data stream as a bitstream, which you will almost always do when recieving a bitstream that you sent previously. Use false for _copyData to simply point to the existing data (used for pure reading). Use true if you want to make an internal copy, either to save the data or if you plan to change it.

Write functions
The write functions write data to the bitstream at the end of the bitstream. You should use the analogous Read to get the data back out.

WriteCompressed functions
The write functions write data to the bitstream at the end of the bitstream. You should use the analogous ReadCompressed to get the data back out

Read functions
The read functions read data already in the bitstream, in order from beginning to end. The read function returns false if there is no more data in the bitstream.

ReadCompressed functions
The read compressed functions read data already in the bitstream, in order from beginning to end, written with WriteCompressed. The ReadCompressed function returns false if there is no more data in the bitstream.

GetNumberOfBitsUsed
GetNumberOfBytesUsed
Gives you the number of bytes or bits written.

GetData
Gives you a pointer to the internal data of the bitstream. This is a (char*) allocated with malloc and is presented in case you need direct assess to the data.
See Also
Index
Creating Packets
Receiving Packets