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 <base/system.h>
4
5#include "compression.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 0;
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 0;
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 0;
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 0;
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 *pSrc = (unsigned char *)pSrc_;
73 const unsigned char *pSrcEnd = pSrc + SrcSize;
74 int *pDst = (int *)pDst_;
75 const int *pDstEnd = pDst + DstSize / sizeof(int);
76 while(pSrc < pSrcEnd)
77 {
78 if(pDst >= pDstEnd)
79 return -1;
80 pSrc = CVariableInt::Unpack(pSrc, pInOut: pDst, SrcSize: pSrcEnd - pSrc);
81 if(!pSrc)
82 return -1;
83 pDst++;
84 }
85 return (long)((unsigned char *)pDst - (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 *pSrc = (int *)pSrc_;
93 unsigned char *pDst = (unsigned char *)pDst_;
94 const unsigned char *pDstEnd = pDst + DstSize;
95 SrcSize /= sizeof(int);
96 while(SrcSize)
97 {
98 pDst = CVariableInt::Pack(pDst, i: *pSrc, DstSize: pDstEnd - pDst);
99 if(!pDst)
100 return -1;
101 SrcSize--;
102 pSrc++;
103 }
104 return (long)(pDst - (unsigned char *)pDst_);
105}
106