commit ac3a118010b8fad81abffbb0f1bb077303c88eaa Author: Natty Date: Fri Dec 22 23:13:07 2023 +0100 Initial proof of concept diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..381b8e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea +/cmake-build-* +/build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b24fe51 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.26) +project(rle C) + +set(CMAKE_C_STANDARD 23) + +add_executable(rle main.c) diff --git a/main.c b/main.c new file mode 100644 index 0000000..94b1695 --- /dev/null +++ b/main.c @@ -0,0 +1,110 @@ +#include +#include +#include + +#define GLYPHS_IN_LOOKUP 4 +#define GLYPH_SIZE (CHAR_BIT) + +unsigned char* encode(const unsigned char* data, const size_t len, size_t* out_len) +{ + // This can be further optimized for size, I was just lazy + unsigned char* rle = calloc(len + 1, sizeof(unsigned char)); + + if (len == 0) { + *out_len = 0; + return rle; + } + + *out_len = 1; + unsigned char bit = 0; + for (size_t i = 0; i < len; ++i) { + unsigned char val = data[i >> 3] & ((unsigned char) (1 << (7 - i & 7))); + // A bit flip has occurred in the stream + if (!val ^ !bit) { + bit = !bit; + (*out_len)++; + } + + (*(rle + *out_len - 1))++; + } + + return rle; +} + +struct lookup_val { + unsigned char position : 5; + unsigned char offset : 3; +}; + +// Just assume lookup is the correct length +void fill_lookup(struct lookup_val* lookup, const unsigned char* rle, const size_t rle_len) { + size_t current = 0; + size_t rest = GLYPH_SIZE; + for (size_t i = 0; i < rle_len; ++i) { + while (rest >= GLYPH_SIZE) { + rest -= GLYPH_SIZE; + lookup[current].position = i; + lookup[current].offset = rest; + current++; + } + + rest += rle[i]; + } +} + +void lookup_glyph(const unsigned char* rle, + const struct lookup_val* lookup, + const char glyph, + unsigned char* glyph_data_out, + const size_t glyph_data_len) { + const unsigned char* rle_current = &rle[lookup[glyph].position]; + unsigned char current = *rle_current - lookup[glyph].offset; + unsigned char bit = 0; + for (size_t i = 0; i < glyph_data_len; ++i) { + if (!current) { + rle_current++; + current = *rle_current; + bit = !bit; + } + + glyph_data_out[i >> 3] |= bit << (unsigned char) (7 - i & 7); + + current--; + } +} + +int main() +{ + // The encoding can be done externally, just like building the lookup table + unsigned char data[] = { 0b01000101, 0b1110001, 0b00010010, 0b000100010 }; + size_t rle_len; + unsigned char* rle = encode(data, sizeof(data) * CHAR_BIT, &rle_len); + + printf("RLE-encoded data: \n"); + for (int i = 0; i < rle_len; ++i) + printf("%dx%s, ", rle[i], i % 2 ? "1" : "0"); + + printf("\n\n"); + + // Index into the lookup table is the ASCII char value we want, here for simplicity only 4 chars + struct lookup_val lookup[GLYPHS_IN_LOOKUP]; + fill_lookup(lookup, rle, rle_len); + + printf("Lookup table: \n"); + for (int i = 0; i < GLYPHS_IN_LOOKUP; ++i) + printf("%d, %d \n", lookup[i].position, lookup[i].offset); + + putchar('\n'); + + unsigned char glyph; + lookup_glyph(rle, lookup, 2, &glyph, GLYPH_SIZE); + + printf("Lookup result: "); + + for (unsigned char i = GLYPH_SIZE; i > 0; --i) { + bool val = glyph & (1 << (i - 1)); + putchar('1' * val + '0' * !val); + } + + putchar('\n'); +}