I've encountered a strange behavior while porting serializer from C# to C which utilizes VLQ and ZigZag under the hood based on integers family. I think the ported code is technically correct, but on practice encoded data is always less for one byte than with the original C# implementation, and this breaks everything: data is serialized/deserialized incorrectly when large integer numbers are used. I've spent 2 days trying to find a mistake, but still, I can't determine it... Any help is much appreciated.
Original implementation in C#:
using System;
using System.Runtime.CompilerServices;
using System.Text;
public class BitBuffer {
private const int defaultCapacity = 8;
private const int stringLengthMax = 512;
private const int stringLengthBits = 9;
private const int bitsASCII = 7;
private const int growFactor = 2;
private const int minGrow = 1;
private int readPosition;
private int nextPosition;
private uint[] chunks;
public BitBuffer(int capacity = defaultCapacity) {
readPosition = 0;
nextPosition = 0;
chunks = new uint[capacity];
}
public int Length {
get {
return ((nextPosition - 1) >> 3) + 1;
}
}
public bool IsFinished {
get {
return nextPosition == readPosition;
}
}
public void Clear() {
readPosition = 0;
nextPosition = 0;
}
public void Add(int numBits, uint value) {
if (numBits < 0)
throw new ArgumentOutOfRangeException("Pushing negative bits");
if (numBits > 32)
throw new ArgumentOutOfRangeException("Pushing too many bits");
int index = nextPosition >> 5;
int used = nextPosition & 0x0000001F;
if ((index + 1) >= chunks.Length)
ExpandArray();
ulong chunkMask = ((1UL << used) - 1);
ulong scratch = chunks[index] & chunkMask;
ulong result = scratch | ((ulong)value << used);
chunks[index] = (uint)result;
chunks[index + 1] = (uint)(result >> 32);
nextPosition += numBits;
}
public uint Read(int numBits) {
uint result = Peek(numBits);
readPosition += numBits;
return result;
}
public uint Peek(int numBits) {
if (numBits < 0)
throw new ArgumentOutOfRangeException("Pushing negative bits");
if (numBits > 32)
throw new ArgumentOutOfRangeException("Pushing too many bits");
int index = readPosition >> 5;
int used = readPosition & 0x0000001F;
ulong chunkMask = ((1UL << numBits) - 1) << used;
ulong scratch = (ulong)chunks[index];
if ((index + 1) < chunks.Length)
scratch |= (ulong)chunks[index + 1] << 32;
ulong result = (scratch & chunkMask) >> used;
return (uint)result;
}
public int ToArray(byte[] data) {
Add(1, 1);
int numChunks = (nextPosition >> 5) + 1;
int length = data.Length;
for (int i = 0; i < numChunks; i++) {
int dataIdx = i * 4;
uint chunk = chunks[i];
if (dataIdx < length)
data[dataIdx] = (byte)(chunk);
if (dataIdx + 1 < length)
data[dataIdx + 1] = (byte)(chunk >> 8);
if (dataIdx + 2 < length)
data[dataIdx + 2] = (byte)(chunk >> 16);
if (dataIdx + 3 < length)
data[dataIdx + 3] = (byte)(chunk >> 24);
}
return Length;
}
public void FromArray(byte[] data, int length) {
int numChunks = (length / 4) + 1;
if (chunks.Length < numChunks)
chunks = new uint[numChunks];
for (int i = 0; i < numChunks; i++) {
int dataIdx = i * 4;
uint chunk = 0;
if (dataIdx < length)
chunk = (uint)data[dataIdx];
if (dataIdx + 1 < length)
chunk = chunk | (uint)data[dataIdx + 1] << 8;
if (dataIdx + 2 < length)
chunk = chunk | (uint)data[dataIdx + 2] << 16;
if (dataIdx + 3 < length)
chunk = chunk | (uint)data[dataIdx + 3] << 24;
chunks[i] = chunk;
}
int positionInByte = FindHighestBitPosition(data[length - 1]);
nextPosition = ((length - 1) * 8) + (positionInByte - 1);
readPosition = 0;
}
public BitBuffer AddBool(bool value) {
Add(1, value ? 1U : 0U);
return this;
}
public bool ReadBool() {
return Read(1) > 0;
}
public bool PeekBool() {
return Peek(1) > 0;
}
public BitBuffer AddByte(byte value) {
Add(8, value);
return this;
}
public byte ReadByte() {
return (byte)Read(8);
}
public byte PeekByte() {
return (byte)Peek(8);
}
public BitBuffer AddShort(short value) {
AddInt(value);
return this;
}
public short ReadShort() {
return (short)ReadInt();
}
public short PeekShort() {
return (short)PeekInt();
}
public BitBuffer AddUShort(ushort value) {
AddUInt(value);
return this;
}
public ushort ReadUShort() {
return (ushort)ReadUInt();
}
public ushort PeekUShort() {
return (ushort)PeekUInt();
}
public BitBuffer AddInt(int value) {
uint zigzag = (uint)((value << 1) ^ (value >> 31));
AddUInt(zigzag);
return this;
}
public int ReadInt() {
uint value = ReadUInt();
int zagzig = (int)((value >> 1) ^ (-(value & 1)));
return zagzig;
}
public int PeekInt() {
uint value = PeekUInt();
int zagzig = (int)((value >> 1) ^ (-(value & 1)));
return zagzig;
}
public BitBuffer AddUInt(uint value) {
uint buffer = 0x0u;
do {
buffer = value & 0x7Fu;
value >>= 7;
if (value > 0)
buffer |= 0x80u;
Add(8, buffer);
}
while (value > 0);
return this;
}
public uint ReadUInt() {
uint buffer = 0x0u;
uint value = 0x0u;
int shift = 0;
do {
buffer = Read(8);
value |= (buffer & 0x7Fu) << shift;
shift += 7;
}
while ((buffer & 0x80u) > 0);
return value;
}
public uint PeekUInt() {
int tempPosition = readPosition;
uint value = ReadUInt();
readPosition = tempPosition;
return value;
}
public BitBuffer AddString(string value) {
if (value == null)
throw new ArgumentNullException("value");
uint length = (uint)value.Length;
if (value.Length > stringLengthMax)
length = (uint)stringLengthMax;
Add(stringLengthBits, length);
for (int i = 0; i < length; i++) {
Add(bitsASCII, ToASCII(value[i]));
}
return this;
}
public string ReadString() {
StringBuilder builder = new StringBuilder();
uint length = Read(stringLengthBits);
for (int i = 0; i < length; i++) {
builder.Append((char)Read(bitsASCII));
}
return builder.ToString();
}
public override string ToString() {
StringBuilder builder = new StringBuilder();
for (int i = chunks.Length - 1; i >= 0; i--) {
builder.Append(Convert.ToString(chunks[i], 2).PadLeft(32, '0'));
}
StringBuilder spaced = new StringBuilder();
for (int i = 0; i < builder.Length; i++) {
spaced.Append(builder[i]);
if (((i + 1) % 8) == 0)
spaced.Append(" ");
}
return spaced.ToString();
}
private void ExpandArray() {
int newCapacity = (chunks.Length * growFactor) + minGrow;
uint[] newChunks = new uint[newCapacity];
Array.Copy(chunks, newChunks, chunks.Length);
chunks = newChunks;
}
private static int FindHighestBitPosition(byte data) {
int shiftCount = 0;
while (data > 0) {
data >>= 1;
shiftCount++;
}
return shiftCount;
}
private static byte ToASCII(char character) {
byte value = 0;
try {
value = Convert.ToByte(character);
}
catch (OverflowException) {
throw new Exception("Cannot convert to ASCII: " + character);
}
if (value > 127)
throw new Exception("Cannot convert to ASCII: " + character);
return value;
}
}
Ported implementation in C:
#ifndef BITBUFFER_H
#define BITBUFFER_H
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define BITBUFFER_VERSION_MAJOR 1
#define BITBUFFER_VERSION_MINOR 0
#define BITBUFFER_VERSION_PATCH 0
#define BITBUFFER_DEFAULT_CAPACITY 8
#define BITBUFFER_STRING_LENGTH_MAX 512
#define BITBUFFER_STRING_LENGTH_BITS 9
#define BITBUFFER_BITS_ASCII 7
#define BITBUFFER_GROW_FACTOR 2
#define BITBUFFER_MIN_GROW 1
#define BITBUFFER_OK 0
#define BITBUFFER_ERROR -1
#define BITBUFFER_TOO_MANY_BITS_ERROR 500
#if defined(_WIN32) && defined(BITBUFFER_DLL)
#define BITBUFFER_API __declspec(dllexport)
#else
#define BITBUFFER_API extern
#endif
// API
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _BitBuffer {
int readPosition;
int nextPosition;
uint32_t* chunks;
int length;
} BitBuffer;
// Public API
BITBUFFER_API int bitbuffer_create(BitBuffer*);
BITBUFFER_API int bitbuffer_create_capacity(BitBuffer*, uint32_t);
BITBUFFER_API int bitbuffer_destroy(BitBuffer*);
BITBUFFER_API int bitbuffer_length(BitBuffer*);
BITBUFFER_API bool bitbuffer_is_finished(BitBuffer*);
BITBUFFER_API int bitbuffer_clear(BitBuffer*);
BITBUFFER_API int bitbuffer_add(BitBuffer*, int, uint32_t);
BITBUFFER_API int bitbuffer_read(BitBuffer*, int);
BITBUFFER_API uint32_t bitbuffer_peek(BitBuffer*, int);
BITBUFFER_API int bitbuffer_to_array(BitBuffer*, uint8_t*, int);
BITBUFFER_API void bitbuffer_from_array(BitBuffer*, const uint8_t*, int);
BITBUFFER_API int bitbuffer_add_bool(BitBuffer*, bool);
BITBUFFER_API bool bitbuffer_read_bool(BitBuffer*);
BITBUFFER_API bool bitbuffer_peek_bool(BitBuffer*);
BITBUFFER_API int bitbuffer_add_byte(BitBuffer*, uint8_t);
BITBUFFER_API uint8_t bitbuffer_read_byte(BitBuffer*);
BITBUFFER_API uint8_t bitbuffer_peek_byte(BitBuffer*);
BITBUFFER_API int bitbuffer_add_short(BitBuffer*, int16_t);
BITBUFFER_API int16_t bitbuffer_read_short(BitBuffer*);
BITBUFFER_API int16_t bitbuffer_peek_short(BitBuffer*);
BITBUFFER_API int bitbuffer_add_ushort(BitBuffer*, uint16_t);
BITBUFFER_API uint16_t bitbuffer_read_ushort(BitBuffer*);
BITBUFFER_API uint16_t bitbuffer_peek_ushort(BitBuffer*);
BITBUFFER_API int bitbuffer_add_int(BitBuffer*, int);
BITBUFFER_API int bitbuffer_read_int(BitBuffer*);
BITBUFFER_API int bitbuffer_peek_int(BitBuffer*);
BITBUFFER_API int bitbuffer_add_uint(BitBuffer*, uint32_t);
BITBUFFER_API uint32_t bitbuffer_read_uint(BitBuffer*);
BITBUFFER_API uint32_t bitbuffer_peek_uint(BitBuffer*);
// Private API
static void bitbuffer_expand_array(BitBuffer*);
static int bitbuffer_find_highest_bit_position(uint8_t);
static uint8_t bitbuffer_to_ascii(char);
#ifdef __cplusplus
}
#endif
#if defined(BITBUFFER_IMPLEMENTATION) && !defined(BITBUFFER_IMPLEMENTATION_DONE)
#define BITBUFFER_IMPLEMENTATION_DONE
#ifdef __cplusplus
extern "C" {
#endif
// Functions
int bitbuffer_create(BitBuffer* bitbuffer) {
return bitbuffer_create_capacity(bitbuffer, BITBUFFER_DEFAULT_CAPACITY);
}
int bitbuffer_create_capacity(BitBuffer* bitbuffer, uint32_t capacity) {
assert(bitbuffer != NULL);
if (bitbuffer == NULL)
return BITBUFFER_ERROR;
bitbuffer->readPosition = 0;
bitbuffer->nextPosition = 0;
bitbuffer->chunks = malloc(capacity * sizeof(uint32_t));
bitbuffer->length = capacity;
return bitbuffer->chunks != NULL ? BITBUFFER_OK : BITBUFFER_ERROR;
}
int bitbuffer_destroy(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
if (bitbuffer == NULL)
return BITBUFFER_ERROR;
free(bitbuffer->chunks);
bitbuffer->chunks = NULL;
return BITBUFFER_OK;
}
int bitbuffer_length(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return ((bitbuffer->nextPosition - 1) >> 3) + 1;
}
bool bitbuffer_is_finished(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return bitbuffer->nextPosition == bitbuffer->readPosition;
}
int bitbuffer_clear(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
if (bitbuffer == NULL)
return BITBUFFER_ERROR;
bitbuffer->nextPosition = 0;
bitbuffer->readPosition = 0;
bitbuffer->length = 0;
return BITBUFFER_OK;
}
int bitbuffer_add(BitBuffer* bitbuffer, int numBits, uint32_t value) {
assert(bitbuffer != NULL);
assert(numBits >= 0);
assert(numBits <= 32);
if (bitbuffer == NULL)
return BITBUFFER_ERROR;
if (numBits > 32)
return BITBUFFER_TOO_MANY_BITS_ERROR;
int index = bitbuffer->nextPosition >> 5;
int used = bitbuffer->nextPosition & 0x0000001F;
if ((index + 1) >= bitbuffer->length)
bitbuffer_expand_array(bitbuffer);
uint64_t chunkMask = ((1UL << used) - 1);
uint64_t scratch = bitbuffer->chunks[index] & chunkMask;
uint64_t result = scratch | ((uint64_t)value << used);
bitbuffer->chunks[index] = (uint32_t)result;
bitbuffer->chunks[index + 1] = (uint32_t)(result >> 32);
bitbuffer->nextPosition += numBits;
return BITBUFFER_OK;
}
int bitbuffer_read(BitBuffer* bitbuffer, int numBits) {
uint32_t result = bitbuffer_peek(bitbuffer, numBits);
bitbuffer->readPosition += numBits;
return result;
}
uint32_t bitbuffer_peek(BitBuffer* bitbuffer, int numBits) {
assert(bitbuffer != NULL);
assert(numBits >= 0);
assert(numBits <= 32);
int index = bitbuffer->readPosition >> 5;
int used = bitbuffer->readPosition & 0x0000001F;
uint64_t chunkMask = ((1UL << numBits) - 1) << used;
uint64_t scratch = (uint64_t)bitbuffer->chunks[index];
if ((index + 1) < bitbuffer->length)
scratch |= (uint64_t)bitbuffer->chunks[index + 1] << 32;
uint64_t result = (scratch & chunkMask) >> used;
return (uint32_t)result;
}
int bitbuffer_to_array(BitBuffer* bitbuffer, uint8_t* data, int length) {
assert(bitbuffer != NULL);
assert(data != NULL);
assert(length > 0);
bitbuffer_add(bitbuffer, 1, 1);
int numChunks = (bitbuffer->nextPosition >> 5) + 1;
for (int i = 0; i < numChunks; i++) {
int dataIdx = i * 4;
uint32_t chunk = bitbuffer->chunks[i];
if (dataIdx < length)
data[dataIdx] = (uint8_t)(chunk);
if (dataIdx + 1 < length)
data[dataIdx + 1] = (uint8_t)(chunk >> 8);
if (dataIdx + 2 < length)
data[dataIdx + 2] = (uint8_t)(chunk >> 16);
if (dataIdx + 3 < length)
data[dataIdx + 3] = (uint8_t)(chunk >> 24);
}
return bitbuffer_length(bitbuffer);
}
void bitbuffer_from_array(BitBuffer* bitbuffer, const uint8_t* data, int length) {
assert(bitbuffer != NULL);
assert(data != NULL);
assert(length > 0);
int numChunks = (length / 4) + 1;
if (bitbuffer->length < numChunks)
bitbuffer->chunks = realloc(bitbuffer->chunks, numChunks * sizeof(uint32_t));
for (int i = 0; i < numChunks; i++) {
int dataIdx = i * 4;
uint32_t chunk = 0;
if (dataIdx < length)
chunk = (uint32_t)data[dataIdx];
if (dataIdx + 1 < length)
chunk = chunk | (uint32_t)data[dataIdx + 1] << 8;
if (dataIdx + 2 < length)
chunk = chunk | (uint32_t)data[dataIdx + 2] << 16;
if (dataIdx + 3 < length)
chunk = chunk | (uint32_t)data[dataIdx + 3] << 24;
bitbuffer->chunks[i] = chunk;
}
int positionInByte = bitbuffer_find_highest_bit_position(data[length - 1]);
bitbuffer->nextPosition = ((length - 1) * 8) + (positionInByte - 1);
bitbuffer->readPosition = 0;
}
inline int bitbuffer_add_bool(BitBuffer* bitbuffer, bool value) {
assert(bitbuffer != NULL);
return bitbuffer_add(bitbuffer, 1, value ? 1U : 0U);
}
inline bool bitbuffer_read_bool(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return bitbuffer_read(bitbuffer, 1) > 0;
}
inline bool bitbuffer_peek_bool(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return bitbuffer_peek(bitbuffer, 1) > 0;
}
inline int bitbuffer_add_byte(BitBuffer* bitbuffer, uint8_t value) {
assert(bitbuffer != NULL);
return bitbuffer_add(bitbuffer, 8, value);
}
inline uint8_t bitbuffer_read_byte(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return (uint8_t)bitbuffer_read(bitbuffer, 8);
}
inline uint8_t bitbuffer_peek_byte(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return (uint8_t)bitbuffer_peek(bitbuffer, 8);
}
inline int bitbuffer_add_short(BitBuffer* bitbuffer, int16_t value) {
assert(bitbuffer != NULL);
return bitbuffer_add_int(bitbuffer, value);
}
inline int16_t bitbuffer_read_short(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return (int16_t)bitbuffer_read_int(bitbuffer);
}
inline int16_t bitbuffer_peek_short(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return (int16_t)bitbuffer_peek_int(bitbuffer);
}
inline int bitbuffer_add_ushort(BitBuffer* bitbuffer, uint16_t value) {
assert(bitbuffer != NULL);
return bitbuffer_add_uint(bitbuffer, value);
}
inline uint16_t bitbuffer_read_ushort(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return (uint16_t)bitbuffer_read_uint(bitbuffer);
}
inline uint16_t bitbuffer_peek_ushort(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
return (uint16_t)bitbuffer_peek_uint(bitbuffer);
}
inline int bitbuffer_add_int(BitBuffer* bitbuffer, int value) {
assert(bitbuffer != NULL);
uint32_t zigzag = (uint32_t)((value << 1) ^ (value >> 31));
return bitbuffer_add_uint(bitbuffer, zigzag);
}
inline int bitbuffer_read_int(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
uint32_t value = bitbuffer_read_uint(bitbuffer);
int zagzig = (int)((value >> 1) ^ (-(value & 1)));
return zagzig;
}
inline int bitbuffer_peek_int(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
uint32_t value = bitbuffer_peek_uint(bitbuffer);
int zagzig = (int)((value >> 1) ^ (-(value & 1)));
return zagzig;
}
inline int bitbuffer_add_uint(BitBuffer* bitbuffer, uint32_t value) {
assert(bitbuffer != NULL);
int status = BITBUFFER_OK;
uint32_t buffer = 0x0u;
do {
buffer = value & 0x7Fu;
value >>= 7;
if (value > 0)
buffer |= 0x80u;
status = bitbuffer_add(bitbuffer, 8, buffer);
}
while (value > 0);
return status;
}
inline uint32_t bitbuffer_read_uint(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
uint32_t buffer = 0x0u;
uint32_t value = 0x0u;
int shift = 0;
do {
buffer = bitbuffer_read(bitbuffer, 8);
value |= (buffer & 0x7Fu) << shift;
shift += 7;
}
while ((buffer & 0x80u) > 0);
return value;
}
inline uint32_t bitbuffer_peek_uint(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
int tempPosition = bitbuffer->readPosition;
uint32_t value = bitbuffer_read_uint(bitbuffer);
bitbuffer->readPosition = tempPosition;
return value;
}
static void bitbuffer_expand_array(BitBuffer* bitbuffer) {
assert(bitbuffer != NULL);
int newCapacity = (bitbuffer->length * BITBUFFER_GROW_FACTOR) + BITBUFFER_MIN_GROW;
uint32_t* oldChunks = bitbuffer->chunks;
uint32_t* newChunks = malloc(newCapacity * sizeof(uint32_t));
memcpy(newChunks, oldChunks, bitbuffer->length * sizeof(uint32_t));
bitbuffer->chunks = newChunks;
bitbuffer->length = newCapacity;
free(oldChunks);
}
static int bitbuffer_find_highest_bit_position(uint8_t data) {
int shiftCount = 0;
while (data > 0) {
data >>= 1;
shiftCount++;
}
return shiftCount;
}
static uint8_t bitbuffer_to_ascii(char character) {
char value = 0;
return value;
}
#ifdef __cplusplus
}
#endif
#endif // BITBUFFER_IMPLEMENTATION
#endif // BITBUFFER_H
Testbed for C#:
private static void Main() {
byte[] buffer;
BitBuffer outbound = new BitBuffer(128);
BitBuffer inbound = new BitBuffer(128);
ushort peer = 999;
bool accelerated = true;
uint speed = 9999999;
byte flag = byte.MaxValue;
ushort color = 512;
int integer = -int.MaxValue;
outbound.AddUShort(peer)
.AddBool(accelerated)
.AddUInt(speed)
.AddByte(flag)
.AddUShort(color)
.AddInt(integer);
buffer = new byte[outbound.Length];
int length = outbound.ToArray(buffer);
Console.WriteLine("Outbound length: " + outbound.Length + " bytes");
inbound.FromArray(buffer, outbound.Length);
Console.WriteLine("Inbound length: " + inbound.Length + " bytes");
Console.WriteLine("Peer: " + inbound.ReadUShort() + ", Accelerated: " + inbound.ReadBool() + ", Speed: " + inbound.ReadUInt() + ", Flag: " + inbound.ReadByte() + ", Color: " + HalfPrecision.Decompress(inbound.ReadUShort()) + ", Integer: " + inbound.ReadInt());
Console.WriteLine("Is finished: " + inbound.IsFinished);
}
Testbed for C:
int main() {
uint8_t* buffer;
BitBuffer outbound;
BitBuffer inbound;
bitbuffer_create_capacity(&outbound, 128);
bitbuffer_create_capacity(&inbound, 128);
uint16_t peer = 999;
bool accelerated = true;
uint32_t speed = 9999999;
uint8_t flag = UCHAR_MAX;
uint16_t color = 512;
int integer = -INT_MAX;
bitbuffer_add_ushort(&outbound, peer);
bitbuffer_add_bool(&outbound, accelerated);
bitbuffer_add_uint(&outbound, speed);
bitbuffer_add_byte(&outbound, flag);
bitbuffer_add_ushort(&outbound, color);
bitbuffer_add_int(&outbound, integer);
buffer = malloc(bitbuffer_length(&outbound));
int length = bitbuffer_to_array(&outbound, buffer, bitbuffer_length(&outbound));
printf("Outbound length: %d bytes\n", length);
bitbuffer_from_array(&inbound, buffer, length);
printf("Inbound length: %d bytes\n", bitbuffer_length(&inbound));
peer = bitbuffer_read_ushort(&inbound);
accelerated = bitbuffer_read_bool(&inbound);
speed = bitbuffer_read_uint(&inbound);
flag = bitbuffer_read_byte(&inbound);
color = bitbuffer_read_ushort(&inbound);
integer = bitbuffer_read_int(&inbound);
printf("Peer: %d, Accelerated: %d, Speed: %d, Flag: %d, Color: %d, Integer: %d\n", peer, accelerated, speed, flag, color, integer);
printf("Is finished: %d", bitbuffer_is_finished(&inbound));
free(buffer);
bitbuffer_destroy(&outbound);
bitbuffer_destroy(&inbound);
}
One thing to note that small values and booleans work fine: peer
and accelerated
data is correct in the testbed.
User contributions licensed under CC BY-SA 3.0