Skip to content

Instantly share code, notes, and snippets.

@Broxzier
Last active May 26, 2023 22:15
Show Gist options
  • Save Broxzier/ad18cd71cf9701de7f1c7a7540dc2f16 to your computer and use it in GitHub Desktop.
Save Broxzier/ad18cd71cf9701de7f1c7a7540dc2f16 to your computer and use it in GitHub Desktop.
Compact int reader and writer
#include <cstdint>
#include <iostream>
#include <limits>
#include <type_traits>
int GetBitValue(const std::uint8_t* data, int offset)
{
constexpr auto BitsPerByte = std::numeric_limits<std::decay_t<decltype(*data)>>::digits;
static_assert(BitsPerByte != 0, "Unknown bit size for type");
auto byteIndex = offset / BitsPerByte;
auto bitIndex = offset % BitsPerByte;
auto byte = data[byteIndex];
return byte >> (BitsPerByte - 1 - bitIndex) & 1;
}
int SeekBitPattern(const std::uint8_t* data, int startPosition, std::uint8_t pattern, std::uint_fast8_t numBits)
{
constexpr auto BitsPerByte = std::numeric_limits<std::decay_t<decltype(*data)>>::digits;
static_assert(BitsPerByte != 0, "Unknown bit size for type");
// In the current implementation, numBits may not be higher than 8
if (numBits == 0 || numBits > BitsPerByte)
return -1;
const auto mask = (1u << numBits) - 1;
// Start off by reading the first numBits bits
int value;
if (startPosition % BitsPerByte + numBits <= BitsPerByte)
{ // initial bits are all within the first byte
auto byte = data[startPosition / BitsPerByte];
value = (byte >> (BitsPerByte - (startPosition % BitsPerByte) - numBits)) & mask;
}
else
{ // A portion of the bits are in the next byte
// TODO: Size check
auto byte1 = data[startPosition / BitsPerByte];
auto byte2 = data[startPosition / BitsPerByte + 1];
auto shiftL = startPosition % BitsPerByte + numBits - BitsPerByte;
value = byte1 << shiftL | byte2 >> (BitsPerByte - shiftL) & mask;
}
// Iterate over the following bits to try find the pattern
auto bitIndex = 0;
while (value != pattern)
{
// TODO: Size check
const auto nextBitIndex = startPosition + bitIndex + numBits;
value <<= 1;
value &= mask;
value |= GetBitValue(data, nextBitIndex);
bitIndex++;
}
return startPosition + bitIndex;
}
int main()
{
// The first byte tells us how many numbers are in the rest of the array starting from the second byte
std::uint8_t input[] = {
//4, 0b11010101, 0b01010000, 0b01101000, 0b11101010, 0b11010010, 0b01100001, 0b10010000, // 289000, 100, 0, 10
//6, 0b10100010, 0b01110100, 0b00001111, 0b01000110, 0b10101001, 0b01110100, 0b10011100, 0b00000000, // 39, 65, 88, 53, 23, 6
8, 0b10100000, 0b00010010, 0b11101000, 0b11011010, 0b01111011, 0b01000110, 0b01110100, 0b01110111, 0b00110110, 0b10000110, 0b00000000, // 30, 25, 43, 60, 87, 91, 12, 74
};
const auto numberCount = input[0];
auto* data = &input[1];
// Seek the first "00" occurrence
auto position = 0;
for (int i = 0; i < numberCount; i++) // TODO: Loop while "00" bits are found, or simply while there is data
{
auto end = SeekBitPattern(data, position, 0b00, 2);
// Parse the bits up to this position into a number
// Each 1 adds 1, each 0 adds 2
int numBits = 1; // The "00" part itself counts as 1, so we start with 1
for (int i = position; i < end; i++)
{
numBits += GetBitValue(data, i) ? 1 : 2;
}
// Set the range (position and end) for the next part to read
// Skip the "00" part we sought before
position = end + 2;
end = position + numBits;
// Read and parse the number of bits right after the "00" to an integer
// The most significant bit is always 1, otherwise it wouldn't have been saved anyway
int num = 1;
while (position < end)
{
num <<= 1;
num |= GetBitValue(data, position);
position++;
}
// Subtract two from the parsed number to get the actual value stored. This is necessary, because the numbers 0 and 1 couldn't be stored otherwise.
num -= 2;
std::cout << num << std::endl;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment