things!
This commit is contained in:
+154
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user