yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie HermaszewskaMove switch statement bodies to their own lines (#5493)b118451e3

master
8.4 KiB310 linesraw
1#include "slang-byte-encode-util.h"
2
3namespace Slang
4{
5
6// Descriptions of algorithms here...
7// https://github.com/stoklund/varint
8
9#if SLANG_LITTLE_ENDIAN && SLANG_UNALIGNED_ACCESS
10// Testing on i7, unaligned access is around 40% faster
11#define SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS 1
12#endif
13
14#ifndef SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS
15#define SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS 0
16#endif
17
18#define SLANG_REPEAT_2(n) n, n
19#define SLANG_REPEAT_4(n) SLANG_REPEAT_2(n), SLANG_REPEAT_2(n)
20#define SLANG_REPEAT_8(n) SLANG_REPEAT_4(n), SLANG_REPEAT_4(n)
21#define SLANG_REPEAT_16(n) SLANG_REPEAT_8(n), SLANG_REPEAT_8(n)
22#define SLANG_REPEAT_32(n) SLANG_REPEAT_16(n), SLANG_REPEAT_16(n)
23#define SLANG_REPEAT_64(n) SLANG_REPEAT_32(n), SLANG_REPEAT_32(n)
24#define SLANG_REPEAT_128(n) SLANG_REPEAT_64(n), SLANG_REPEAT_64(n)
25
26/* static */ const int8_t ByteEncodeUtil::s_msb8[256] = {
27    -1,
28    0,
29    SLANG_REPEAT_2(1),
30    SLANG_REPEAT_4(2),
31    SLANG_REPEAT_8(3),
32    SLANG_REPEAT_16(4),
33    SLANG_REPEAT_32(5),
34    SLANG_REPEAT_64(6),
35    SLANG_REPEAT_128(7),
36};
37
38/* static */ size_t ByteEncodeUtil::calcEncodeLiteSizeUInt32(const uint32_t* in, size_t num)
39{
40    size_t totalNumEncodeBytes = 0;
41
42    for (size_t i = 0; i < num; i++)
43    {
44        const uint32_t v = in[i];
45
46        if (v < kLiteCut1)
47        {
48            totalNumEncodeBytes += 1;
49        }
50        else if (v <= kLiteCut1 + 255 * (kLiteCut2 - 1 - kLiteCut1))
51        {
52            totalNumEncodeBytes += 2;
53        }
54        else
55        {
56            totalNumEncodeBytes += calcNonZeroMsByte32(v) + 2;
57        }
58    }
59    return totalNumEncodeBytes;
60}
61
62/* static */ size_t ByteEncodeUtil::encodeLiteUInt32(
63    const uint32_t* in,
64    size_t num,
65    uint8_t* encodeOut)
66{
67    uint8_t* encodeStart = encodeOut;
68
69    for (size_t i = 0; i < num; ++i)
70    {
71        uint32_t v = in[i];
72
73        if (v < kLiteCut1)
74        {
75            *encodeOut++ = uint8_t(v);
76        }
77        else if (v <= kLiteCut1 + 255 * (kLiteCut2 - 1 - kLiteCut1))
78        {
79            v -= kLiteCut1;
80
81            encodeOut[0] = uint8_t(kLiteCut1 + (v >> 8));
82            encodeOut[1] = uint8_t(v);
83            encodeOut += 2;
84        }
85        else
86        {
87            uint8_t* encodeOutStart = encodeOut++;
88            while (v)
89            {
90                *encodeOut++ = uint8_t(v);
91                v >>= 8;
92            }
93            // Finally write the size to the start
94            const int numBytes = int(encodeOut - encodeOutStart);
95            encodeOutStart[0] = uint8_t(kLiteCut2 + (numBytes - 2));
96        }
97    }
98    return size_t(encodeOut - encodeStart);
99}
100
101/* static */ void ByteEncodeUtil::encodeLiteUInt32(
102    const uint32_t* in,
103    size_t num,
104    List<uint8_t>& encodeArrayOut)
105{
106    // Make sure there is at least enough space for all bytes
107    encodeArrayOut.setCount(num);
108
109    uint8_t* encodeOut = encodeArrayOut.begin();
110    uint8_t* encodeOutEnd = encodeArrayOut.end();
111
112    for (size_t i = 0; i < num; ++i)
113    {
114        // Check if we need some more space
115        if (encodeOut + kMaxLiteEncodeUInt32 > encodeOutEnd)
116        {
117            const size_t offset = size_t(encodeOut - encodeArrayOut.begin());
118
119            const UInt oldCapacity = encodeArrayOut.getCapacity();
120
121            // Make some more space
122            encodeArrayOut.reserve(oldCapacity + (oldCapacity >> 1) + kMaxLiteEncodeUInt32);
123            // Make the size the capacity
124            const UInt capacity = encodeArrayOut.getCapacity();
125            encodeArrayOut.setCount(capacity);
126
127            encodeOut = encodeArrayOut.begin() + offset;
128            encodeOutEnd = encodeArrayOut.end();
129        }
130
131        uint32_t v = in[i];
132
133        if (v < kLiteCut1)
134        {
135            *encodeOut++ = uint8_t(v);
136        }
137        else if (v <= kLiteCut1 + 255 * (kLiteCut2 - 1 - kLiteCut1))
138        {
139            v -= kLiteCut1;
140
141            encodeOut[0] = uint8_t(kLiteCut1 + (v >> 8));
142            encodeOut[1] = uint8_t(v);
143            encodeOut += 2;
144        }
145        else
146        {
147            uint8_t* encodeOutStart = encodeOut++;
148            while (v)
149            {
150                *encodeOut++ = uint8_t(v);
151                v >>= 8;
152            }
153            // Finally write the size to the start
154            const int numBytes = int(encodeOut - encodeOutStart);
155            encodeOutStart[0] = uint8_t(kLiteCut2 + (numBytes - 2));
156        }
157    }
158
159    encodeArrayOut.setCount(UInt(encodeOut - encodeArrayOut.begin()));
160    encodeArrayOut.compress();
161}
162
163/* static */ int ByteEncodeUtil::encodeLiteUInt32(uint32_t in, uint8_t out[kMaxLiteEncodeUInt32])
164{
165    // 0-184        1 byte    value = B0
166    // 185 - 248    2 bytes   value = 185 + 256 * (B0 - 185) + B1
167    // 249 - 255    3 - 9 bytes value = (B0 - 249 + 2) little - endian bytes following B0.
168
169    if (in < kLiteCut1)
170    {
171        out[0] = uint8_t(in);
172        return 1;
173    }
174    else if (in <= kLiteCut1 + 255 * (kLiteCut2 - 1 - kLiteCut1))
175    {
176        in -= kLiteCut1;
177
178        out[0] = uint8_t(kLiteCut1 + (in >> 8));
179        out[1] = uint8_t(in);
180        return 2;
181    }
182    else
183    {
184        int numBytes = 1;
185        while (in)
186        {
187            out[numBytes++] = uint8_t(in);
188            in >>= 8;
189        }
190        // Finally write the size
191        out[0] = uint8_t(kLiteCut2 + (numBytes - 2));
192        return numBytes;
193    }
194}
195
196static const uint32_t s_unalignedUInt32Mask[5] = {
197    0x00000000,
198    0x000000ff,
199    0x0000ffff,
200    0x00ffffff,
201    0xffffffff,
202};
203
204// Decode the >= kLiteCut2.
205// in is pointing past the first byte.
206// Only valid numBytesRemaining is 2, 3, or 4
207SLANG_FORCE_INLINE static uint32_t _decodeLiteCut2UInt32(const uint8_t* in, int numBytesRemaining)
208{
209    uint32_t value = 0;
210#if SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS
211    switch (numBytesRemaining)
212    {
213    case 2:
214        value = *(const uint16_t*)in;
215        break;
216    case 3:
217        value = (uint32_t(in[2]) << 16) | (uint32_t(in[1]) << 8) | uint32_t(in[0]);
218        break;
219    case 4:
220        value = *(const uint32_t*)in;
221        break;
222    default:
223        break;
224    }
225#else
226    // This works on all cpus although slower
227    value = in[0];
228    switch (numBytesRemaining)
229    {
230    case 4:
231        value |= uint32_t(in[3]) << 24; /* fall thru */
232    case 3:
233        value |= uint32_t(in[2]) << 16; /* fall thru */
234    case 2:
235        value |= uint32_t(in[1]) << 8; /* fall thru */
236    case 1:
237        break;
238    }
239#endif
240    return value;
241}
242
243/* static */ int ByteEncodeUtil::decodeLiteUInt32(const uint8_t* in, uint32_t* out)
244{
245    uint8_t b0 = *in++;
246    if (b0 < kLiteCut1)
247    {
248        *out = uint32_t(b0);
249        return 1;
250    }
251    else if (b0 < kLiteCut2)
252    {
253        uint8_t b1 = *in++;
254        *out = kLiteCut1 + b1 + (uint32_t(b0 - kLiteCut1) << 8);
255        return 2;
256    }
257    else
258    {
259        const int numBytesRemaining = b0 - kLiteCut2 + 2 - 1;
260        *out = _decodeLiteCut2UInt32(in, numBytesRemaining);
261        return numBytesRemaining + 1;
262    }
263}
264
265/* static */ size_t ByteEncodeUtil::decodeLiteUInt32(
266    const uint8_t* encodeIn,
267    size_t numValues,
268    uint32_t* valuesOut)
269{
270    const uint8_t* encodeStart = encodeIn;
271
272    for (size_t i = 0; i < numValues; ++i)
273    {
274        uint8_t b0 = *encodeIn++;
275        if (b0 < kLiteCut1)
276        {
277            valuesOut[i] = uint32_t(b0);
278        }
279        else if (b0 < kLiteCut2)
280        {
281            uint8_t b1 = *encodeIn++;
282            valuesOut[i] = kLiteCut1 + b1 + (uint32_t(b0 - kLiteCut1) << 8);
283        }
284        else
285        {
286            const int numBytesRemaining = b0 - kLiteCut2 + 2 - 1;
287
288            // For unaligned access, do not use unaligned access for the last two values,
289            // (3rd last is safe because this value will have at least 2 bytes, followed by at worst
290            // two 1-byte values) otherwise we can access outside the bounds of the encoded array
291            // This prevents memory validation tools from causing an exception here
292            if (SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS && i < numValues - 2)
293            {
294                const uint32_t mask = s_unalignedUInt32Mask[numBytesRemaining];
295                // const uint32_t mask = ~(uint32_t(0xffffff00) << ((numBytesRemaining - 1) * 8));
296                valuesOut[i] = (*(const uint32_t*)encodeIn) & mask;
297            }
298            else
299            {
300                valuesOut[i] = _decodeLiteCut2UInt32(encodeIn, numBytesRemaining);
301            }
302
303            encodeIn += numBytesRemaining;
304        }
305    }
306
307    return size_t(encodeIn - encodeStart);
308}
309
310} // namespace Slang