#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); }