Technical int packing deep dive
This page is covering a technical dissection of the int packing code.
With some examples to better visualize it.
There is a chance you never have to fully understand int packing to
achieve your goals related to tw networking. This section is a bit weird.
I recommend reading the higher level docs on that in the
fundamentals section.
If you already red that and still want to dive deeper you are correct here.
Other projects test code as reference
When trying to figure out how the packer should behave it can help to look at the tests that were written for it.
- C++ tests of the ddnet project
- python tests of the twnet_parser project
- ruby tests of the teeworlds_network project
- rust tests of the teeint project
The C packer code with print statements
Here the official teeworlds Pack()
method annotated with some debug statements
to better visualize what is happening.
#include "stdio.h"
void printbincharpad(char c)
{
for (int i = 7; i >= 0; --i)
{
putchar( (c & (1 << i)) ? '1' : '0' );
}
}
void str_hex(char *dst, int dst_size, const void *data, int data_size)
{
static const char hex[] = "0123456789ABCDEF";
int b;
for(b = 0; b < data_size && b < dst_size/4-4; b++)
{
dst[b*3] = hex[((const unsigned char *)data)[b]>>4];
dst[b*3+1] = hex[((const unsigned char *)data)[b]&0xf];
dst[b*3+2] = ' ';
dst[b*3+3] = 0;
}
}
void print_bin(const char * pBuf, int size)
{
for(int i = 0; i < size; i++)
{
printbincharpad(pBuf[i]);
putchar(' ');
}
putchar('\n');
}
unsigned char *Pack(unsigned char *pDst, int i)
{
*pDst = 0;
if(i < 0)
{
*pDst |= 0x40; // set sign bit
i = ~i;
}
*pDst |= i & 0x3F; // pack 6bit into dst
i >>= 6; // discard 6 bits
while(i)
{
*pDst |= 0x80; // set extend bit
pDst++;
*pDst = i & 0x7F; // pack 7bit
i >>= 7; // discard 7 bits
}
pDst++;
return pDst;
}
void verbose_pack(int i)
{
int num_bytes = 1;
if(i > 63 || i < -64)
num_bytes = 2;
char aBuf[1024];
char aHex[1024];
Pack((unsigned char *)aBuf, i);
str_hex(aHex, sizeof(aHex), aBuf, num_bytes);
printf("Pack(%d)\n => hex=%s\n => bin=", i, aHex);
print_bin(aBuf, num_bytes);
printf(" => fmt=ESDDDDDD ");
for(int k = 1; k < num_bytes; k++)
printf("EDDDDDDD ");
if(num_bytes > 1)
puts("\n when building the int keep in mind it is little endian!\n");
else
puts("\n");
}
int main()
{
verbose_pack(1);
verbose_pack(2);
verbose_pack(3);
verbose_pack(-1);
verbose_pack(-2);
verbose_pack(-3);
verbose_pack(63);
verbose_pack(64);
verbose_pack(65);
verbose_pack(-63);
verbose_pack(-64);
verbose_pack(-65);
verbose_pack(-66);
verbose_pack(-67);
return 0;
}
It will output the following
Pack(1)
=> hex=01
=> bin=00000001
=> fmt=ESDDDDDD
Pack(2)
=> hex=02
=> bin=00000010
=> fmt=ESDDDDDD
Pack(3)
=> hex=03
=> bin=00000011
=> fmt=ESDDDDDD
Pack(-1)
=> hex=40
=> bin=01000000
=> fmt=ESDDDDDD
Pack(-2)
=> hex=41
=> bin=01000001
=> fmt=ESDDDDDD
Pack(-3)
=> hex=42
=> bin=01000010
=> fmt=ESDDDDDD
Pack(63)
=> hex=3F
=> bin=00111111
=> fmt=ESDDDDDD
Pack(64)
=> hex=80 01
=> bin=10000000 00000001
=> fmt=ESDDDDDD EDDDDDDD
when building the int keep in mind it is little endian!
Pack(65)
=> hex=81 01
=> bin=10000001 00000001
=> fmt=ESDDDDDD EDDDDDDD
when building the int keep in mind it is little endian!
Pack(-63)
=> hex=7E
=> bin=01111110
=> fmt=ESDDDDDD
Pack(-64)
=> hex=7F
=> bin=01111111
=> fmt=ESDDDDDD
Pack(-65)
=> hex=C0 01
=> bin=11000000 00000001
=> fmt=ESDDDDDD EDDDDDDD
when building the int keep in mind it is little endian!
Pack(-66)
=> hex=C1 01
=> bin=11000001 00000001
=> fmt=ESDDDDDD EDDDDDDD
when building the int keep in mind it is little endian!
Pack(-67)
=> hex=C2 01
=> bin=11000010 00000001
=> fmt=ESDDDDDD EDDDDDDD
when building the int keep in mind it is little endian!