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 |
10 | unsigned 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 | |
40 | const 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 | |
68 | long 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 | |
88 | long 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 | |