summaryrefslogtreecommitdiffstats
path: root/source/core/slang-byte-encode-util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/core/slang-byte-encode-util.cpp')
-rw-r--r--source/core/slang-byte-encode-util.cpp283
1 files changed, 283 insertions, 0 deletions
diff --git a/source/core/slang-byte-encode-util.cpp b/source/core/slang-byte-encode-util.cpp
new file mode 100644
index 000000000..6eabdc16f
--- /dev/null
+++ b/source/core/slang-byte-encode-util.cpp
@@ -0,0 +1,283 @@
+#include "slang-byte-encode-util.h"
+
+
+
+namespace Slang {
+
+// Descriptions of algorithms here...
+// https://github.com/stoklund/varint
+
+#if SLANG_LITTLE_ENDIAN && SLANG_UNALIGNED_ACCESS
+// Testing on i7, unaligned access is around 40% faster
+# define SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS 1
+#endif
+
+#ifndef SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS
+# define SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS 0
+#endif
+
+#define SLANG_REPEAT_2(n) n, n
+#define SLANG_REPEAT_4(n) SLANG_REPEAT_2(n), SLANG_REPEAT_2(n)
+#define SLANG_REPEAT_8(n) SLANG_REPEAT_4(n), SLANG_REPEAT_4(n)
+#define SLANG_REPEAT_16(n) SLANG_REPEAT_8(n), SLANG_REPEAT_8(n)
+#define SLANG_REPEAT_32(n) SLANG_REPEAT_16(n), SLANG_REPEAT_16(n)
+#define SLANG_REPEAT_64(n) SLANG_REPEAT_32(n), SLANG_REPEAT_32(n)
+#define SLANG_REPEAT_128(n) SLANG_REPEAT_64(n), SLANG_REPEAT_64(n)
+
+/* static */const int8_t ByteEncodeUtil::s_msb8[256] =
+{
+ - 1,
+ 0,
+ SLANG_REPEAT_2(1),
+ SLANG_REPEAT_4(2),
+ SLANG_REPEAT_8(3),
+ SLANG_REPEAT_16(4),
+ SLANG_REPEAT_32(5),
+ SLANG_REPEAT_64(6),
+ SLANG_REPEAT_128(7),
+};
+
+/* static */size_t ByteEncodeUtil::calcEncodeLiteSizeUInt32(const uint32_t* in, size_t num)
+{
+ size_t totalNumEncodeBytes = 0;
+
+ for (size_t i = 0; i < num; i++)
+ {
+ const uint32_t v = in[i];
+
+ if (v < kLiteCut1)
+ {
+ totalNumEncodeBytes += 1;
+ }
+ else if (v <= kLiteCut1 + 255 * (kLiteCut2 - 1 - kLiteCut1))
+ {
+ totalNumEncodeBytes += 2;
+ }
+ else
+ {
+ totalNumEncodeBytes += calcNonZeroMsByte32(v) + 2;
+ }
+ }
+ return totalNumEncodeBytes;
+}
+
+/* static */size_t ByteEncodeUtil::encodeLiteUInt32(const uint32_t* in, size_t num, uint8_t* encodeOut)
+{
+ uint8_t* encodeStart = encodeOut;
+
+ for (size_t i = 0; i < num; ++i)
+ {
+ uint32_t v = in[i];
+
+ if(v < kLiteCut1)
+ {
+ *encodeOut++ = uint8_t(v);
+ }
+ else if (v <= kLiteCut1 + 255 * (kLiteCut2 - 1 - kLiteCut1))
+ {
+ v -= kLiteCut1;
+
+ encodeOut[0] = uint8_t(kLiteCut1 + (v >> 8));
+ encodeOut[1] = uint8_t(v);
+ encodeOut += 2;
+ }
+ else
+ {
+ uint8_t* encodeOutStart = encodeOut++;
+ while (v)
+ {
+ *encodeOut++ = uint8_t(v);
+ v >>= 8;
+ }
+ // Finally write the size to the start
+ const int numBytes = int(encodeOut - encodeOutStart);
+ encodeOutStart[0] = uint8_t(kLiteCut2 + (numBytes - 2));
+ }
+ }
+ return size_t(encodeOut - encodeStart);
+}
+
+/* static */void ByteEncodeUtil::encodeLiteUInt32(const uint32_t* in, size_t num, List<uint8_t>& encodeArrayOut)
+{
+ // Make sure there is at least enough space for all bytes
+ encodeArrayOut.SetSize(num);
+
+ uint8_t* encodeOut = encodeArrayOut.begin();
+ uint8_t* encodeOutEnd = encodeArrayOut.end();
+
+ for (size_t i = 0; i < num; ++i)
+ {
+ // Check if we need some more space
+ if (encodeOut + kMaxLiteEncodeUInt32 > encodeOutEnd)
+ {
+ const size_t offset = size_t(encodeOut - encodeArrayOut.begin());
+
+ const UInt oldCapacity = encodeArrayOut.Capacity();
+
+ // Make some more space
+ encodeArrayOut.Reserve(oldCapacity + (oldCapacity >> 1) + kMaxLiteEncodeUInt32);
+ // Make the size the capacity
+ const UInt capacity = encodeArrayOut.Capacity();
+ encodeArrayOut.SetSize(capacity);
+
+ encodeOut = encodeArrayOut.begin() + offset;
+ encodeOutEnd = encodeArrayOut.end();
+ }
+
+ uint32_t v = in[i];
+
+ if (v < kLiteCut1)
+ {
+ *encodeOut++ = uint8_t(v);
+ }
+ else if (v <= kLiteCut1 + 255 * (kLiteCut2 - 1 - kLiteCut1))
+ {
+ v -= kLiteCut1;
+
+ encodeOut[0] = uint8_t(kLiteCut1 + (v >> 8));
+ encodeOut[1] = uint8_t(v);
+ encodeOut += 2;
+ }
+ else
+ {
+ uint8_t* encodeOutStart = encodeOut++;
+ while (v)
+ {
+ *encodeOut++ = uint8_t(v);
+ v >>= 8;
+ }
+ // Finally write the size to the start
+ const int numBytes = int(encodeOut - encodeOutStart);
+ encodeOutStart[0] = uint8_t(kLiteCut2 + (numBytes - 2));
+ }
+ }
+
+ encodeArrayOut.SetSize(UInt(encodeOut - encodeArrayOut.begin()));
+ encodeArrayOut.Compress();
+}
+
+/* static */int ByteEncodeUtil::encodeLiteUInt32(uint32_t in, uint8_t out[kMaxLiteEncodeUInt32])
+{
+ // 0-184 1 byte value = B0
+ // 185 - 248 2 bytes value = 185 + 256 * (B0 - 185) + B1
+ // 249 - 255 3 - 9 bytes value = (B0 - 249 + 2) little - endian bytes following B0.
+
+ if (in < kLiteCut1)
+ {
+ out[0] = uint8_t(in);
+ return 1;
+ }
+ else if (in <= kLiteCut1 + 255 * (kLiteCut2 - 1 - kLiteCut1))
+ {
+ in -= kLiteCut1;
+
+ out[0] = uint8_t(kLiteCut1 + (in >> 8));
+ out[1] = uint8_t(in);
+ return 2;
+ }
+ else
+ {
+ int numBytes = 1;
+ while (in)
+ {
+ out[numBytes++] = uint8_t(in);
+ in >>= 8;
+ }
+ // Finally write the size
+ out[0] = uint8_t(kLiteCut2 + (numBytes - 2));
+ return numBytes;
+ }
+}
+
+static const uint32_t s_unalignedUInt32Mask[5] =
+{
+ 0x00000000,
+ 0x000000ff,
+ 0x0000ffff,
+ 0x00ffffff,
+ 0xffffffff,
+};
+
+/* static */int ByteEncodeUtil::decodeLiteUInt32(const uint8_t* in, uint32_t* out)
+{
+ uint8_t b0 = *in++;
+ if (b0 < kLiteCut1)
+ {
+ *out = uint32_t(b0);
+ return 1;
+ }
+ else if (b0 < kLiteCut2)
+ {
+ uint8_t b1 = *in++;
+ *out = kLiteCut1 + b1 + (uint32_t(b0 - kLiteCut1) << 8);
+ return 2;
+ }
+ else
+ {
+ int numBytesRemaining = b0 - kLiteCut2 + 2 - 1;
+
+#if SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS
+ //const uint32_t mask = s_unalignedUInt32Mask[numBytesRemaining];
+ const uint32_t mask = ~(uint32_t(0xffffff00) << ((numBytesRemaining - 1) * 8));
+ const uint32_t value = (*(const uint32_t*)in) & mask;
+#else
+ // This works on all cpus although slower
+ uint32_t value = in[0];
+
+ switch (numBytesRemaining)
+ {
+ case 4: value |= uint32_t(in[3]) << 24; /* fall thru */
+ case 3: value |= uint32_t(in[2]) << 16; /* fall thru */
+ case 2: value |= uint32_t(in[1]) << 8; /* fall thru */
+ case 1: break;
+ }
+#endif
+ *out = value;
+ return numBytesRemaining + 1;
+ }
+}
+
+/* static */size_t ByteEncodeUtil::decodeLiteUInt32(const uint8_t* encodeIn, size_t numValues, uint32_t* valuesOut)
+{
+ const uint8_t* encodeStart = encodeIn;
+
+ for (size_t i = 0; i < numValues; ++i)
+ {
+ uint8_t b0 = *encodeIn++;
+ if (b0 < kLiteCut1)
+ {
+ valuesOut[i] = uint32_t(b0);
+ }
+ else if (b0 < kLiteCut2)
+ {
+ uint8_t b1 = *encodeIn++;
+ valuesOut[i] = kLiteCut1 + b1 + (uint32_t(b0 - kLiteCut1) << 8);
+ }
+ else
+ {
+ int numBytesRemaining = b0 - kLiteCut2 + 2 - 1;
+
+#if SLANG_BYTE_ENCODE_USE_UNALIGNED_ACCESS
+ const uint32_t mask = s_unalignedUInt32Mask[numBytesRemaining];
+ //const uint32_t mask = ~(uint32_t(0xffffff00) << ((numBytesRemaining - 1) * 8));
+ const uint32_t value = (*(const uint32_t*)encodeIn) & mask;
+#else
+ // This works on all cpus although slower
+ uint32_t value = encodeIn[0];
+ switch (numBytesRemaining)
+ {
+ case 4: value |= uint32_t(encodeIn[3]) << 24; /* fall thru */
+ case 3: value |= uint32_t(encodeIn[2]) << 16; /* fall thru */
+ case 2: value |= uint32_t(encodeIn[1]) << 8; /* fall thru */
+ case 1: break;
+ }
+#endif
+ valuesOut[i] = value;
+ encodeIn += numBytesRemaining;
+ }
+ }
+
+ return size_t(encodeIn - encodeStart);
+}
+
+} // namespace Slang