Files
brightstone/Common/lzw.c
T
veclavtlica adfae10938 things!
2026-09-11 02:21:21 +03:00

155 lines
4.3 KiB
C

/*
LZW compression that is required for GIF implementation.
https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
*/
#include "lzw.h"
#include "bits.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/* References: */
/* https://www.w3.org/Graphics/GIF/spec-gif89a.txt */
/* https://www.daubnet.com/en/file-format-gif */
/* After it is reached the table is supposed to be reset */
/* As this is known, the upper boundary of memory used is known */
#define COM_LZW_CODEPOINT_LIMIT 8192
#define COM_TABLE_BIT_WIDTH_LIMIT 12 /* typical GIF impl */
#define COM_LZW_OUTPUT_CAP_GROWTH 256
#define COM_LZW_TABLE_CAP_GROW 128
struct com_lzw_table com_lzw_infer_table(const char *datain, uint32_t sizein) {
struct com_lzw_table_entry *table = NULL;
uint32_t table_init_size = 0;
uint32_t table_size = 0;
uint32_t table_cap = 0;
/* Infer initial table from the data */
for (uint32_t i = 0; i < sizein && table_init_size < 255; i++) {
bool code_found = false;
char code = datain[i];
/* Try searching the code */
for (uint32_t t = 0; t < table_init_size; t++) {
if (table[t].code == code) {
code_found = true;
break;
}
}
/* Append non-existing codepoint */
if (!code_found) {
if (table_size >= table_cap) {
/* TODO: Ref to prev table gets missed, memory leak scenario */
if (!(table = realloc(table, sizeof(struct com_lzw_table_entry) *
(table_cap + COM_LZW_TABLE_CAP_GROW))))
goto ERR_ALLOC_INIT_TABLE;
table_cap += COM_LZW_TABLE_CAP_GROW;
}
table[table_size] = (struct com_lzw_table_entry){.code = code};
table_init_size++;
table_size++;
}
}
return (struct com_lzw_table){
.table = table,
.cap = table_cap,
.size = table_size,
.init_size = table_init_size,
.continuous = false,
};
ERR_ALLOC_INIT_TABLE:
if (table_cap > 0)
free(table);
return (struct com_lzw_table){0};
}
void com_lzw_free_table(struct com_lzw_table *table) {
if (!table->table)
return;
free(table->table);
table->size = 0;
table->cap = 0;
table->init_size = 0;
}
/* For GIFs, color should already be collapsed to indices at this point */
bool com_lzw_compress(const struct com_lzw_table *table, const char *datain,
uint32_t sizein, char **dataout, uint32_t *sizeout) {
assert(datain && dataout && sizeout && sizein > 0);
assert(*dataout == NULL && *sizeout == 0);
/* TODO: Make sure the table is cleared */
/* Encode step */
uint8_t codesize = com_bits_needed(table->init_size) + 1;
assert(codesize < COM_TABLE_BIT_WIDTH_LIMIT);
uint32_t cur_table_idx =
0; /* Head table in which vector we should be looking into */
uint32_t feedback =
0; /* How many bytes feeded from datain to the current string */
char *output = NULL;
uint32_t output_size = 0;
uint32_t output_cap = 0;
uint8_t output_bitshift = 0;
for (uint32_t i = 0; i < sizein; i++) {
char code = datain[i];
if (feedback == 0) {
/* Should not be possible not to find the first code */
for (uint32_t t = 0; t < table->init_size; t++) {
if (table->table[t].code == code) {
cur_table_idx = t;
feedback++;
}
}
} else {
// for (uint32_t v = 0; v < table->table[cur_table_idx].tree_vector_size;
// v++) {
// if (table->table[table->table[cur_table_idx].tree_vector[v]].code ==
// code) {
// cur_table_idx = table->table[cur_table_idx].tree_vector[v];
// feedback++;
// continue;
// }
// }
/* Emit output, drop the string */
assert(cur_table_idx < (1 << codesize));
assert(cur_table_idx < table->size);
if (output_size == output_cap && output_bitshift + codesize > 8) {
/* TODO: catch alloc failure */
output = realloc(output, output_cap + COM_LZW_OUTPUT_CAP_GROWTH);
output_cap += COM_LZW_OUTPUT_CAP_GROWTH;
}
// uint16_t code = cur_table_idx;
// uint8_t bits_to_write = codesize;
// while (bits_to_write > 0) {
// bits_to_write -=
// }
/* Return to prev char as it wasn't processed */
feedback = 0;
i--;
}
}
*sizeout = output_size;
*dataout = output;
return true;
}