#include "decode.h"
// Bit Packing Variables
static int code = 0;
static int usedBits = 0;
static int bitLen = 9;
static void cleanUp(DecodeEntry *table, Stack *stack)
{
free(table);
free(stack -> data);
free(stack);
}
Stack *initStack(int stack_size)
{
Stack *stack = (Stack *)malloc(sizeof(Stack));
if (stack == NULL)
{
fprintf(stderr, "Memory allocation for stack failed");
return NULL;
}
stack -> data = (unsigned char *)malloc(stack_size * sizeof(unsigned char));
if (stack -> data == NULL)
{
fprintf(stderr, "Memory allocation for stack data failed");
return NULL;
}
stack -> top = -1;
stack -> max_size = stack_size;
return stack;
}
void push(Stack *stack, unsigned char value)
{
if (stack -> top < stack -> max_size - 1)
{
stack -> top++;
stack -> data[stack -> top] = value;
} else {
fprintf(stderr, "Stack overflow: string too long (max_size=%d)\n", stack->max_size);
exit(1);
}
}
unsigned char pop(Stack *stack)
{
if (stack -> top >= 0)
{
unsigned char value = stack -> data[stack -> top];
stack -> top--;
return value;
}
return 0;
}
int isStackEmpty(Stack *stack)
{
return stack -> top == -1;
}
DecodeEntry *initDecodeTable(int table_size)
{
struct DecodeEntry* table = (struct DecodeEntry*)calloc(table_size, sizeof(struct DecodeEntry));
if (table == NULL)
{
fprintf(stderr, "Failed to allocate table\n");
return NULL;
}
for (int i = 0; i < 256; i++)
{
table[i].prefix = EMPTY;
table[i].character = (unsigned char)i;
}
return table;
}
int pruneDecodeTable(DecodeEntry **table, int table_size)
{
bool *usedAsPrefix = calloc(table_size, sizeof(bool));
if (usedAsPrefix == NULL)
{
fprintf(stderr, "Memory allocation for prefix tracker failed\n");
return -1;
}
for (int code = 256; code < table_size; code++)
{
DecodeEntry item = (*table)[code];
usedAsPrefix[item.prefix] = true;
}
int *codeMap = calloc(table_size, sizeof(int));
if (codeMap == NULL)
{
fprintf(stderr, "Memory allocation for code mapping table failed\n");
free(usedAsPrefix);
return -1;
}
for (int i = 0; i < table_size; i++)
{
codeMap[i] = EMPTY;
}
DecodeEntry *newTable = initDecodeTable(table_size);
if (newTable == NULL)
{
fprintf(stderr, "Memory allocation for new array failed\n");
free(usedAsPrefix);
free(codeMap);
return -1;
}
int nextCode = 256;
for (int code = 256; code < table_size; code++)
{
if (usedAsPrefix[code])
{
DecodeEntry item = (*table)[code];
int findPrefix = item.prefix;
if (findPrefix > 255 && codeMap[findPrefix] != EMPTY)
{
findPrefix = codeMap[findPrefix];
}
codeMap[code] = nextCode;
if (item.prefix > 255 && codeMap[item.prefix] != EMPTY)
{
item.prefix = codeMap[item.prefix];
}
newTable[nextCode++] = item;
}
}
free(usedAsPrefix);
free(codeMap);
free(*table);
*table = newTable;
return nextCode;
}
void decode(void)
{
int maxBits, pruning;
char *stage = getenv("STAGE");
if (!stage)
{
stage = "3";
}
if (!strcmp(stage, "3"))
{
unsigned char coderConfig;
if (fread(&coderConfig, sizeof(unsigned char), 1, stdin) != 1)
{
fprintf(stderr, "Error: failed to read maxBits/pruning configuration");
exit(1);
}
maxBits = coderConfig & 0x7F;
pruning = coderConfig >> 7;
} else if (!strcmp(stage, "2"))
{
if (fscanf(stdin, "%d", &maxBits) != 1 ||
fscanf(stdin, "%d", &pruning) != 1)
{
return;
}
} else {
maxBits = DEFAULT_BITS;
pruning = 0;
}
int table_size = (1 << maxBits); // 2^MAXBITS
DecodeEntry *table = initDecodeTable(table_size);
Stack *stack = initStack(table_size);
int nextCode = 256;
int newC = -1;
int C, oldC;
int finalK;
if (strcmp(stage, "3"))
{
// Stage 1 and 2
if (fscanf(stdin, "%d", &newC) != 1)
{
cleanUp(table, stack);
return;
}
oldC = newC;
C = newC;
finalK = table[C].character;
fputc(finalK, stdout);
while (fscanf(stdin, "%d", &newC) == 1)
{
if (nextCode >= table_size && pruning)
{
nextCode = pruneDecodeTable(&table, table_size);
if (nextCode == -1)
{
cleanUp(table, stack);
exit(1);
}
}
C = newC;
if (C >= nextCode)
{
C = oldC;
push(stack, finalK);
}
while (C > 255)
{
push(stack, table[C].character);
C = table[C].prefix;
}
finalK = table[C].character;
fputc(finalK, stdout);
while (!isStackEmpty(stack))
{
fputc(pop(stack), stdout);
}
if (nextCode < table_size)
{
table[nextCode].prefix = oldC;
table[nextCode].character = finalK;
nextCode++;
}
oldC = newC;
}
} else {
// Stage 3
unsigned char byte;
newC = -1;
while (newC == -1 && fread(&byte, sizeof(unsigned char), 1, stdin))
{
newC = bitUnpacker(byte);
}
oldC = newC;
C = newC;
finalK = table[C].character;
fputc(finalK, stdout);
while (fread(&byte, sizeof(unsigned char), 1, stdin))
{
newC = bitUnpacker(byte);
if (newC == -1) continue;
if (nextCode >= table_size && pruning)
{
nextCode = pruneDecodeTable(&table, table_size);
if (nextCode == -1)
{
cleanUp(table, stack);
exit(1);
}
}
C = newC;
if (C >= nextCode)
{
C = oldC;
push(stack, finalK);
}
while (C > 255)
{
push(stack, table[C].character);
C = table[C].prefix;
}
finalK = table[C].character;
fputc(finalK, stdout);
while (!isStackEmpty(stack))
{
fputc(pop(stack), stdout);
}
if (nextCode < table_size)
{
table[nextCode].prefix = oldC;
table[nextCode].character = finalK;
nextCode++;
if (nextCode + 1 >> bitLen)
{
if (usedBits > 0 && fread(&byte, sizeof(unsigned char), 1, stdin))
{
bitUnpacker(byte);
code = 0;
usedBits = 0;
}
bitLen++;
}
}
oldC = newC;
}
}
if (getenv("DBG") != NULL)
{
dumpDecodeTable(table, nextCode, table_size);
}
cleanUp(table, stack);
}
int bitUnpacker(unsigned char byte)
{
code = (code << BYTE_SIZE) | byte;
usedBits += BYTE_SIZE;
if (usedBits >= bitLen)
{
int temp = code >> (usedBits - bitLen);
code &= ((1 << (usedBits - bitLen)) - 1);
usedBits -= bitLen;
return temp;
}
return -1;
}
void dumpDecodeTable(struct DecodeEntry *table, int nextCode, int table_size)
{
char filename[256];
snprintf(filename, sizeof(filename), "./DBG.%s", "decode");
FILE* fp = fopen(filename, "w");
if (!fp)
{
fprintf(stderr, "Error opening file %s: %s (errno: %d)\n",
filename, strerror(errno), errno);
return;
}
fprintf(fp, "CODE\tPREF\tCHAR\tString\n");
for (int code = 0; code < nextCode; code++)
{
char string[table_size];
int len = 0;
int curr = code;
while (curr > 255 && len < table_size)
{
string[len++] = table[curr].character;
curr = table[curr].prefix;
}
if (len < table_size) {
string[len++] = table[curr].character;
}
fprintf(fp, "%d\t%d\t%d\t",
code, table[code].prefix, table[code].character);
for (int i = len - 1; i >= 0; i--)
{
fputc(string[i], fp);
}
fputc('\n', fp);
}
fclose(fp);
}