1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "compression.h"
4
5#include <base/system.h>
6
7#include <iterator> // std::size
8
9// Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign
10unsigned char *CVariableInt::Pack(unsigned char *pDst, int i, int DstSize)
11{
12 if(DstSize <= 0)
13 return nullptr;
14
15 DstSize--;
16 *pDst = 0;
17 if(i < 0)
18 {
19 *pDst |= 0x40; // set sign bit
20 i = ~i;
21 }
22
23 *pDst |= i & 0x3F; // pack 6bit into dst
24 i >>= 6; // discard 6 bits
25 while(i)
26 {
27 if(DstSize <= 0)
28 return nullptr;
29 *pDst |= 0x80; // set extend bit
30 DstSize--;
31 pDst++;
32 *pDst = i & 0x7F; // pack 7bit
33 i >>= 7; // discard 7 bits
34 }
35
36 pDst++;
37 return pDst;
38}
39
40const unsigned char *CVariableInt::Unpack(const unsigned char *pSrc, int *pInOut, int SrcSize)
41{
42 if(SrcSize <= 0)
43 return nullptr;
44
45 const int Sign = (*pSrc >> 6) & 1;
46 *pInOut = *pSrc & 0x3F;
47 SrcSize--;
48
49 static const int s_aMasks[] = {0x7F, 0x7F, 0x7F, 0x0F};
50 static const int s_aShifts[] = {6, 6 + 7, 6 + 7 + 7, 6 + 7 + 7 + 7};
51
52 for(unsigned i = 0; i < std::size(s_aMasks); i++)
53 {
54 if(!(*pSrc & 0x80))
55 break;
56 if(SrcSize <= 0)
57 return nullptr;
58 SrcSize--;
59 pSrc++;
60 *pInOut |= (*pSrc & s_aMasks[i]) << s_aShifts[i];
61 }
62
63 pSrc++;
64 *pInOut ^= -Sign; // if(sign) *i = ~(*i)
65 return pSrc;
66}
67
68long CVariableInt::Decompress(const void *pSrc, int SrcSize, void *pDst, int DstSize)
69{
70 dbg_assert(DstSize % sizeof(int) == 0, "invalid bounds");
71
72 const unsigned char *pCharSrc = (unsigned char *)pSrc;
73 const unsigned char *pCharSrcEnd = pCharSrc + SrcSize;
74 int *pIntDst = (int *)pDst;
75 const int *pIntDstEnd = pIntDst + DstSize / sizeof(int); // NOLINT(bugprone-sizeof-expression)
76 while(pCharSrc < pCharSrcEnd)
77 {
78 if(pIntDst >= pIntDstEnd)
79 return -1;
80 pCharSrc = CVariableInt::Unpack(pSrc: pCharSrc, pInOut: pIntDst, SrcSize: pCharSrcEnd - pCharSrc);
81 if(!pCharSrc)
82 return -1;
83 pIntDst++;
84 }
85 return (long)((unsigned char *)pIntDst - (unsigned char *)pDst);
86}
87
88long CVariableInt::Compress(const void *pSrc, int SrcSize, void *pDst, int DstSize)
89{
90 dbg_assert(SrcSize % sizeof(int) == 0, "invalid bounds");
91
92 const int *pIntSrc = (int *)pSrc;
93 unsigned char *pCharDst = (unsigned char *)pDst;
94 const unsigned char *pCharDstEnd = pCharDst + DstSize;
95 SrcSize /= sizeof(int);
96 while(SrcSize)
97 {
98 pCharDst = CVariableInt::Pack(pDst: pCharDst, i: *pIntSrc, DstSize: pCharDstEnd - pCharDst);
99 if(!pCharDst)
100 return -1;
101 SrcSize--;
102 pIntSrc++;
103 }
104 return (long)(pCharDst - (unsigned char *)pDst);
105}
106