#include #include #include #include #define MIN_MATCH 3 #define MAX_MATCH 258 #define WS_SMALL 511 #define WS_LARGE 16383 #define HASH_SIZE 65536 #define HASH_MASK (HASH_SIZE - 1) // v1.7: Huffman dinamico por bloco nos literais (DEFLATE-lite), offset fixo // formato novo no bloco comprimido: type1 + decomp_len + huff_flag + [arvore] + tokens // literais: flag0 + codigo Huffman; matches: igual v1.6; fallback fixed se Huffman nao compensa #define BLOCK_SIZE 65536 // ============================================================================ // INTEGRITY ANCHOR (HONORING MARK ADLER) // ============================================================================ uint32_t calcular_adler32(const uint8_t *data, size_t len) { uint32_t a = 1, b = 0; for (size_t i = 0; i < len; i++) { a = (a + data[i]) % 65521; b = (b + a) % 65521; } return (b << 16) | a; } // ============================================================================ // V1.7 HUFFMAN DINAMICO NOS LITERAIS (canonico, max 15 bits) // ============================================================================ typedef struct { uint8_t is_match; uint8_t lit; uint16_t off; uint8_t use_large; uint16_t len; } Tok17; typedef struct { uint32_t freq; int sym; } FreqSym; typedef struct { uint32_t freq; int left; int right; int sym; } HNode; static int cmp_freq(const void *a, const void *b) { const FreqSym *fa = (const FreqSym*)a, *fb = (const FreqSym*)b; if (fa->freq != fb->freq) return (fa->freq < fb->freq) ? -1 : 1; return fa->sym - fb->sym; } static void huff_assign_depth(HNode *nodes, int idx, int depth, uint8_t *lens) { if (idx < 0) return; if (nodes[idx].sym >= 0) { if (depth < 1) depth = 1; if (depth > 15) depth = 16; // marca overflow lens[nodes[idx].sym] = (uint8_t)depth; return; } huff_assign_depth(nodes, nodes[idx].left, depth + 1, lens); huff_assign_depth(nodes, nodes[idx].right, depth + 1, lens); } // freq[256] -> lens[256]; retorna nsyms ou -1 se overflow >15 static int build_huffman_lengths(const uint32_t *freq, uint8_t *lens) { memset(lens, 0, 256); FreqSym fs[256]; int n = 0; for (int i = 0; i < 256; i++) if (freq[i]) { fs[n].freq = freq[i]; fs[n].sym = i; n++; } if (n == 0) return 0; if (n == 1) { lens[fs[0].sym] = 1; return 1; } qsort(fs, n, sizeof(FreqSym), cmp_freq); // arvore via array de nos (folhas + internos) HNode nodes[511]; for (int i = 0; i < n; i++) { nodes[i].freq = fs[i].freq; nodes[i].left = nodes[i].right = -1; nodes[i].sym = fs[i].sym; } int nnodes = n; // lista de disponiveis (indices): mantem vetor simples int avail[511]; int an = n; for (int i = 0; i < n; i++) avail[i] = i; while (an > 1) { // acha dois menores int m1 = -1, m2 = -1; for (int i = 0; i < an; i++) { int idx = avail[i]; if (m1 < 0 || nodes[idx].freq < nodes[m1].freq) { m2 = m1; m1 = idx; } else if (m2 < 0 || nodes[idx].freq < nodes[m2].freq) { m2 = idx; } } // remove m1,m2 de avail int nav[511]; int nn = 0; for (int i = 0; i < an; i++) if (avail[i] != m1 && avail[i] != m2) nav[nn++] = avail[i]; memcpy(avail, nav, nn * sizeof(int)); an = nn; nodes[nnodes].freq = nodes[m1].freq + nodes[m2].freq; nodes[nnodes].left = m1; nodes[nnodes].right = m2; nodes[nnodes].sym = -1; avail[an++] = nnodes; nnodes++; if (nnodes >= 511) break; } int root = avail[0]; huff_assign_depth(nodes, root, 0, lens); int cnt = 0; for (int i = 0; i < 256; i++) if (lens[i]) { if (lens[i] > 15) return -1; cnt++; } return cnt; } static void build_canonical(const uint8_t *lens, uint32_t *codes) { memset(codes, 0, 256 * sizeof(uint32_t)); int bl_count[16]; memset(bl_count, 0, sizeof(bl_count)); for (int i = 0; i < 256; i++) if (lens[i]) bl_count[lens[i]]++; uint32_t code = 0; uint32_t next_code[16]; memset(next_code, 0, sizeof(next_code)); for (int bits = 1; bits <= 15; bits++) { code = (code + bl_count[bits - 1]) << 1; next_code[bits] = code; } // ordena por (len, sym) int order[256]; int on = 0; for (int l = 1; l <= 15; l++) for (int s = 0; s < 256; s++) if (lens[s] == l) order[on++] = s; for (int i = 0; i < on; i++) { int s = order[i]; codes[s] = next_code[lens[s]]++; } } // decoder bit-a-bit via tabela canonica (lento mas simples e correto) typedef struct { uint8_t lens[256]; uint32_t codes[256]; int nsyms; } HuffDec; static void huffdec_build(HuffDec *d, const uint8_t *lens) { memcpy(d->lens, lens, 256); build_canonical(lens, d->codes); d->nsyms = 0; for (int i = 0; i < 256; i++) if (lens[i]) d->nsyms++; } // ============================================================================ // IN-MEMORY BIT-STREAM (para decidir comprimido vs stored) // ============================================================================ typedef struct { uint8_t *buf; size_t cap; size_t bit_pos; } MemBitStream; void mbs_init(MemBitStream *stream, uint8_t *buffer, size_t capacity) { stream->buf = buffer; stream->cap = capacity; stream->bit_pos = 0; memset(buffer, 0, capacity); } void mbs_write_bit(MemBitStream *stream, int bit) { size_t byte_idx = stream->bit_pos / 8; int bit_idx = 7 - (int)(stream->bit_pos % 8); if (byte_idx >= stream->cap) return; if (bit) stream->buf[byte_idx] |= (uint8_t)(1 << bit_idx); else stream->buf[byte_idx] &= (uint8_t)~(1 << bit_idx); stream->bit_pos++; } void mbs_write_bits(MemBitStream *stream, uint32_t value, int n) { for (int i = n - 1; i >= 0; i--) mbs_write_bit(stream, (value >> i) & 1); } // ============================================================================ // FILE BIT WRITER (sem nested function, compila MSVC + GCC) // ============================================================================ typedef struct { FILE *f; uint8_t cur; int nbits; } BitWriter; void bw_init(BitWriter *w, FILE *f) { w->f = f; w->cur = 0; w->nbits = 0; } void bw_write_bit(BitWriter *w, int bit) { w->cur = (uint8_t)((w->cur << 1) | (bit & 1)); w->nbits++; if (w->nbits == 8) { fputc(w->cur, w->f); w->cur = 0; w->nbits = 0; } } void bw_write_bits(BitWriter *w, uint32_t v, int n) { for (int i = n - 1; i >= 0; i--) bw_write_bit(w, (v >> i) & 1); } void bw_flush(BitWriter *w) { if (w->nbits > 0) { w->cur <<= (8 - w->nbits); fputc(w->cur, w->f); w->cur = 0; w->nbits = 0; } } // ============================================================================ // FILE BIT READER (sem ftell, sem global, sem perder sync) // ============================================================================ typedef struct { FILE *f; uint8_t buf; int cnt; int eof; } BitReader; void br_init(BitReader *r, FILE *f) { r->f = f; r->buf = 0; r->cnt = 0; r->eof = 0; } int br_read_bit(BitReader *r) { if (r->cnt == 0) { int c = fgetc(r->f); if (c == EOF) { r->eof = 1; return 0; } r->buf = (uint8_t)c; r->cnt = 8; } r->cnt--; return (r->buf >> r->cnt) & 1; } uint32_t br_read_bits(BitReader *r, int n) { uint32_t v = 0; for (int i = 0; i < n; i++) v = (v << 1) | (uint32_t)br_read_bit(r); return v; } // descarta resto do byte parcial -> usar SOMENTE antes de bloco stored void br_align(BitReader *r) { r->cnt = 0; } static int huffdec_decode(BitReader *r, HuffDec *d) { uint32_t code = 0; for (int len = 1; len <= 15; len++) { if (r->eof) return -1; code = (code << 1) | (uint32_t)br_read_bit(r); for (int s = 0; s < 256; s++) { if (d->lens[s] == len && d->codes[s] == code) return s; } } return -1; } // ============================================================================ // HASH ENGINE // ============================================================================ static inline uint32_t quick_hash(const uint8_t *ptr) { return ((uint32_t)(ptr[0] << 8) ^ (uint32_t)(ptr[1] << 4) ^ (uint32_t)ptr[2]) & HASH_MASK; } // head/prev como int64 para arquivos grandes, -1 = vazio // retorna match length, preenche *best_off e *use_large int find_match_v15(uint8_t *in, uint64_t global_pos, uint64_t block_end, int64_t *head, int64_t *prev, int *best_off, int *use_large) { int best_len_s = 0, best_off_s = 0; int best_len_l = 0, best_off_l = 0; if (block_end <= global_pos) return 0; uint64_t remain = block_end - global_pos; uint64_t max = remain > MAX_MATCH ? MAX_MATCH : remain; if (max < MIN_MATCH) return 0; uint32_t h = quick_hash(&in[global_pos]); int64_t cur = head[h]; int chain = 0; while (cur != -1 && chain++ < 256) { uint64_t match_pos = (uint64_t)cur; if (match_pos >= global_pos) break; // seguranca uint64_t distance = global_pos - match_pos; if (distance > (uint64_t)WS_LARGE) break; if (distance == 0) break; // sem prefetch OOB: calcula direto (seguro: match_pos+l < block_end, global_pos+l < block_end) int l = 0; // match_pos+l < total garantido se match_pos < global_pos e l <= max while ((uint64_t)l < max && in[match_pos + l] == in[global_pos + l]) l++; if (distance <= (uint64_t)WS_SMALL && l >= MIN_MATCH && l > best_len_s) { best_len_s = l; best_off_s = (int)distance; } if (l >= MIN_MATCH && l > best_len_l) { best_len_l = l; best_off_l = (int)distance; } cur = prev[match_pos & (uint64_t)WS_LARGE]; } if (best_len_s < MIN_MATCH && best_len_l < MIN_MATCH) return 0; if (best_len_s < MIN_MATCH) { *best_off = best_off_l; *use_large = 1; return best_len_l; } if (best_len_l < MIN_MATCH) { *best_off = best_off_s; *use_large = 0; return best_len_s; } if (((best_len_l - best_len_s) * 9) > 5) { *best_off = best_off_l; *use_large = 1; return best_len_l; } else { *best_off = best_off_s; *use_large = 0; return best_len_s; } } static void hash_insert(uint8_t *buf, uint64_t total_len, uint64_t pos, int64_t *head, int64_t *prev) { if (pos + MIN_MATCH > total_len) return; uint32_t h = quick_hash(&buf[pos]); prev[pos & (uint64_t)WS_LARGE] = head[h]; head[h] = (int64_t)pos; } // ============================================================================ // COMPRESSAO COM STORED BLOCK ESCAPE (pulo se nao compressivel) // Formato v1.5: // header 12 bytes: uint64 total_len + uint32 adler // por bloco: // 1 bit tipo // se 1 (comprimido): 32 bits decomp_len + tokens (para exatamente decomp_len bytes) // se 0 (stored): flush byte + uint32 raw_len LE + raw bytes // ============================================================================ int executar_compressao(const char* entrada, const char* saida) { FILE *fin = fopen(entrada, "rb"); if (!fin) { printf("Error opening input payload.\n"); return 1; } fseek(fin, 0, SEEK_END); long ft = ftell(fin); uint64_t total_len = ft < 0 ? 0 : (uint64_t)ft; fseek(fin, 0, SEEK_SET); uint8_t *buf = NULL; if (total_len > 0) { buf = (uint8_t*)malloc((size_t)total_len); if (!buf) { printf("Memory allocation failure.\n"); fclose(fin); return 1; } if (fread(buf, 1, (size_t)total_len, fin) != (size_t)total_len) { printf("Read fault.\n"); fclose(fin); free(buf); return 1; } } fclose(fin); uint32_t checksum = calcular_adler32(buf ? buf : (uint8_t*)"", (size_t)total_len); FILE *fout = fopen(saida, "wb"); if (!fout) { printf("Error creating compressed file.\n"); free(buf); return 1; } fwrite(&total_len, sizeof(uint64_t), 1, fout); fwrite(&checksum, sizeof(uint32_t), 1, fout); if (total_len == 0) { fclose(fout); free(buf); return 0; } int64_t *head = (int64_t*)malloc(HASH_SIZE * sizeof(int64_t)); int64_t *prev = (int64_t*)malloc((WS_LARGE + 1) * sizeof(int64_t)); uint8_t *comp_block_buf = (uint8_t*)malloc(BLOCK_SIZE * 3); Tok17 *toks = (Tok17*)malloc(BLOCK_SIZE * sizeof(Tok17)); if (!head || !prev || !comp_block_buf || !toks) { printf("Memory allocation failure.\n"); fclose(fout); free(buf); free(head); free(prev); free(comp_block_buf); free(toks); return 1; } for (int i = 0; i < HASH_SIZE; i++) head[i] = -1; for (int i = 0; i <= WS_LARGE; i++) prev[i] = -1; BitWriter bw; bw_init(&bw, fout); uint64_t global_p = 0; while (global_p < total_len) { uint64_t block_start = global_p; uint64_t block_end = block_start + BLOCK_SIZE; if (block_end > total_len) block_end = total_len; uint32_t block_raw_len = (uint32_t)(block_end - block_start); MemBitStream mbs; mbs_init(&mbs, comp_block_buf, BLOCK_SIZE * 3); // --- v1.6 lazy parsing gerando tokens (para Huffman) --- size_t ntoks = 0; uint32_t freq[256]; memset(freq, 0, sizeof(freq)); size_t fixed_payload_bits = 0; uint64_t internal_p = block_start; while (internal_p < block_end) { int off = 0, use_large = 0, ml = 0; if (internal_p + MIN_MATCH <= block_end) ml = find_match_v15(buf, internal_p, block_end, head, prev, &off, &use_large); int cur_cost = 0, cur_save = -1000000; if (ml >= MIN_MATCH) { int bits_offset = use_large ? 14 : 9; cur_cost = (ml >= 10) ? (1 + 1 + 3 + bits_offset + 8) : (1 + 1 + 3 + bits_offset); cur_save = ml * 9 - cur_cost; } if (ml >= MIN_MATCH && cur_save > 0) { hash_insert(buf, total_len, internal_p, head, prev); int off2 = 0, use_large2 = 0, ml2 = 0; if (internal_p + 1 + MIN_MATCH <= block_end) ml2 = find_match_v15(buf, internal_p + 1, block_end, head, prev, &off2, &use_large2); if (ml2 >= MIN_MATCH) { int bits2 = use_large2 ? 14 : 9; int cost2 = (ml2 >= 10) ? (1 + 1 + 3 + bits2 + 8) : (1 + 1 + 3 + bits2); int save2 = ml2 * 9 - cost2; if (save2 > cur_save) { toks[ntoks].is_match = 0; toks[ntoks].lit = buf[internal_p]; ntoks++; freq[buf[internal_p]]++; fixed_payload_bits += 9; internal_p++; continue; } } toks[ntoks].is_match = 1; toks[ntoks].off = (uint16_t)off; toks[ntoks].use_large = (uint8_t)use_large; toks[ntoks].len = (uint16_t)ml; ntoks++; fixed_payload_bits += (size_t)cur_cost; internal_p++; for (int k = 1; k < ml; k++) { hash_insert(buf, total_len, internal_p, head, prev); internal_p++; } continue; } toks[ntoks].is_match = 0; toks[ntoks].lit = buf[internal_p]; ntoks++; freq[buf[internal_p]]++; fixed_payload_bits += 9; hash_insert(buf, total_len, internal_p, head, prev); internal_p++; } // --- v18a REP decide: simula historico rep[3]={1,8,32} sobre toks --- int rep_en = 0; { int rh[3] = {1,8,32}; size_t save_bits = 0; size_t nm = 0; for (size_t t = 0; t < ntoks; t++) { if (!toks[t].is_match) continue; nm++; int o = toks[t].off; int hit = (o==rh[0]||o==rh[1]||o==rh[2]) ? 1 : 0; if (hit) { int bo = toks[t].use_large ? 14 : 9; save_bits += (size_t)(bo - 2); /* troca offset por 2b idx */ } // atualiza historico igual encoder/decoder if (hit) { int code=-1; for(int r=0;r<3;r++) if(o==rh[r]){code=r;break;} if(code>0){int tmp=rh[code]; for(int r=code;r>0;r--) rh[r]=rh[r-1]; rh[0]=tmp;} } else { rh[2]=rh[1]; rh[1]=rh[0]; rh[0]=o; } } size_t overhead = nm * 1 + 1; // 1b rep_flag por match + 1b modo if (save_bits > overhead) rep_en = 1; } // --- Huffman nos literais --- uint8_t lens[256]; uint32_t codes[256]; int nsyms = build_huffman_lengths(freq, lens); size_t huff_payload_bits = 0; size_t huff_overhead_bits = 0; if (nsyms > 0) { build_canonical(lens, codes); for (size_t t = 0; t < ntoks; t++) { if (!toks[t].is_match) huff_payload_bits += 1 + lens[toks[t].lit]; else { int bo = toks[t].use_large ? 14 : 9; size_t base = (size_t)((toks[t].len >= 10) ? (1 + 1 + 3 + bo + 8) : (1 + 1 + 3 + bo)); if (rep_en) { base += 1; /*rep_flag*/ } huff_payload_bits += base; } } if (rep_en) { // subtrai economia: cada hit troca bo bits por 2b int rh2[3] = {1,8,32}; for (size_t t = 0; t < ntoks; t++) { if (!toks[t].is_match) continue; int o = toks[t].off; if (o==rh2[0]||o==rh2[1]||o==rh2[2]) { int bo = toks[t].use_large ? 14 : 9; huff_payload_bits -= (size_t)(bo - 2); int code=-1; for(int r=0;r<3;r++) if(o==rh2[r]){code=r;break;} if(code>0){int tmp=rh2[code]; for(int r=code;r>0;r--) rh2[r]=rh2[r-1]; rh2[0]=tmp;} } else { rh2[2]=rh2[1]; rh2[1]=rh2[0]; rh2[0]=o; } } } huff_payload_bits += 1; // bit rep_en huff_overhead_bits = 1 + 256 + (size_t)nsyms * 4; } else { // sem literais: Huffman inutil, payload igual ao fixed huff_payload_bits = fixed_payload_bits; huff_overhead_bits = (size_t)1 << 30; // forca fixed } { size_t fp = 0; int rh3[3] = {1,8,32}; for (size_t t = 0; t < ntoks; t++) { if (!toks[t].is_match) fp += 1 + 8; else { int bo = toks[t].use_large ? 14 : 9; fp += (size_t)((toks[t].len >= 10) ? (1 + 1 + 3 + bo + 8) : (1 + 1 + 3 + bo)); if (rep_en) fp += 1; int o = toks[t].off; if (rep_en && (o==rh3[0]||o==rh3[1]||o==rh3[2])) { fp -= (size_t)((toks[t].use_large ? 14 : 9) - 2); int code=-1; for(int r=0;r<3;r++) if(o==rh3[r]){code=r;break;} if(code>0){int tmp=rh3[code]; for(int r=code;r>0;r--) rh3[r]=rh3[r-1]; rh3[0]=tmp;} } else if (rep_en) { rh3[2]=rh3[1]; rh3[1]=rh3[0]; rh3[0]=o; } else { rh3[2]=rh3[1]; rh3[1]=rh3[0]; rh3[0]=o; } } } fp += 1; /*rep_en bit*/ fixed_payload_bits = fp; } size_t fixed_block_bits = 1 + 32 + 1 + fixed_payload_bits; size_t huff_block_bits = 1 + 32 + huff_overhead_bits + huff_payload_bits; // estimativa stored: 1 bit tipo + pad + u32 + raw (compara em bytes como v1.5) int fixed_beats_raw = (fixed_payload_bits + 7) / 8 < block_raw_len; // codifica o modo vencedor no mbs (somente payload de tokens; header vai via bw) // mbs contera: [huff_flag 1b] + [arvore?] + tokens int mode_huff = 0; if (nsyms > 0 && huff_block_bits < fixed_block_bits && (huff_block_bits + 7) / 8 < block_raw_len) mode_huff = 1; else if (!fixed_beats_raw) mode_huff = -1; // stored else mode_huff = 0; // fixed if (mode_huff >= 0) { if (mode_huff == 1) { mbs_write_bit(&mbs, 1); mbs_write_bit(&mbs, rep_en); for (int s = 0; s < 256; s++) mbs_write_bit(&mbs, lens[s] ? 1 : 0); for (int s = 0; s < 256; s++) if (lens[s]) mbs_write_bits(&mbs, lens[s], 4); int rhe[3] = {1,8,32}; for (size_t t = 0; t < ntoks; t++) { if (!toks[t].is_match) { mbs_write_bit(&mbs, 0); mbs_write_bits(&mbs, codes[toks[t].lit], lens[toks[t].lit]); } else { int bo = toks[t].use_large ? 14 : 9; int o = toks[t].off; int code=-1; if (rep_en) { for(int r=0;r<3;r++) if(o==rhe[r]){code=r;break;} } mbs_write_bit(&mbs, 1); if (rep_en) { if (code>=0) { mbs_write_bit(&mbs, 0); // rep mbs_write_bits(&mbs, (uint32_t)code, 2); if(code>0){int tmp=rhe[code]; for(int r=code;r>0;r--) rhe[r]=rhe[r-1]; rhe[0]=tmp;} } else { mbs_write_bit(&mbs, 1); // nao-rep mbs_write_bit(&mbs, toks[t].use_large); rhe[2]=rhe[1]; rhe[1]=rhe[0]; rhe[0]=o; } } else { mbs_write_bit(&mbs, toks[t].use_large); rhe[2]=rhe[1]; rhe[1]=rhe[0]; rhe[0]=o; } if (code>=0 && rep_en) { // rep: sem offset, so len } else { // placeholder: len+off escritos abaixo } if (toks[t].len < 10) mbs_write_bits(&mbs, toks[t].len - 3, 3); else mbs_write_bits(&mbs, 7, 3); if (!(rep_en && code>=0)) mbs_write_bits(&mbs, toks[t].off, bo); if (toks[t].len >= 10) mbs_write_bits(&mbs, toks[t].len - 3, 8); } } } else { mbs_write_bit(&mbs, 0); mbs_write_bit(&mbs, rep_en); { int rhf[3] = {1,8,32}; for (size_t t = 0; t < ntoks; t++) { if (!toks[t].is_match) { mbs_write_bit(&mbs, 0); mbs_write_bits(&mbs, toks[t].lit, 8); } else { int bo = toks[t].use_large ? 14 : 9; int o = toks[t].off; int code=-1; if (rep_en) { for(int r=0;r<3;r++) if(o==rhf[r]){code=r;break;} } mbs_write_bit(&mbs, 1); if (rep_en) { if (code>=0) { mbs_write_bit(&mbs, 0); mbs_write_bits(&mbs,(uint32_t)code,2); if(code>0){int tmp=rhf[code]; for(int r=code;r>0;r--) rhf[r]=rhf[r-1]; rhf[0]=tmp;} } else { mbs_write_bit(&mbs, 1); mbs_write_bit(&mbs, toks[t].use_large); rhf[2]=rhf[1]; rhf[1]=rhf[0]; rhf[0]=o; } } else { mbs_write_bit(&mbs, toks[t].use_large); rhf[2]=rhf[1]; rhf[1]=rhf[0]; rhf[0]=o; } if (toks[t].len < 10) mbs_write_bits(&mbs, toks[t].len - 3, 3); else mbs_write_bits(&mbs, 7, 3); if (!(rep_en && code>=0)) mbs_write_bits(&mbs, toks[t].off, bo); if (toks[t].len >= 10) mbs_write_bits(&mbs, toks[t].len - 3, 8); } } } } } size_t total_bits_used = mbs.bit_pos; size_t total_bytes_used = (total_bits_used + 7) / 8; if (mode_huff >= 0 && total_bytes_used < block_raw_len) { // bloco comprimido v1.7: tipo 1 + decomp_len + payload(mbs) bw_write_bit(&bw, 1); bw_write_bits(&bw, block_raw_len, 32); for (size_t bit_idx = 0; bit_idx < total_bits_used; bit_idx++) { size_t byte_pos = bit_idx / 8; int bit_pos = 7 - (int)(bit_idx % 8); bw_write_bit(&bw, (comp_block_buf[byte_pos] >> bit_pos) & 1); } } else { // pulo: bloco stored, nao expande bw_write_bit(&bw, 0); bw_flush(&bw); fwrite(&block_raw_len, sizeof(uint32_t), 1, fout); fwrite(&buf[block_start], 1, block_raw_len, fout); } global_p = block_end; } bw_flush(&bw); fclose(fout); free(buf); free(head); free(prev); free(comp_block_buf); free(toks); printf("LZSS v1.8a REP engine complete.\n"); return 0; } // ============================================================================ // DESCOMPRESSAO // ============================================================================ int executar_descompressao(const char* entrada, const char* saida) { FILE *fin = fopen(entrada, "rb"); if (!fin) { printf("Error opening compressed file.\n"); return 1; } uint64_t total_original_len = 0; uint32_t checksum_original = 0; if (fread(&total_original_len, sizeof(uint64_t), 1, fin) != 1 || fread(&checksum_original, sizeof(uint32_t), 1, fin) != 1) { printf("Header processing fault.\n"); fclose(fin); return 1; } uint8_t *out_buf = NULL; if (total_original_len > 0) { out_buf = (uint8_t*)malloc((size_t)total_original_len); if (!out_buf) { printf("Memory bounds allocation fault.\n"); fclose(fin); return 1; } } else { // arquivo vazio fclose(fin); FILE *fout = fopen(saida, "wb"); if (fout) fclose(fout); uint32_t c = calcular_adler32((uint8_t*)"", 0); if (c == checksum_original) { printf("Decompression Complete. Status: 100%% INTEGRAL (Adler-32 Checked).\n"); return 0; } else { printf("CRITICAL VALIDATION ERROR: Output payload is corrupted.\n"); return 2; } } BitReader br; br_init(&br, fin); uint64_t global_p = 0; while (global_p < total_original_len) { if (br.eof) { printf("Truncated block header.\n"); break; } int block_type = br_read_bit(&br); if (block_type == 1) { uint32_t decomp_len = br_read_bits(&br, 32); if (br.eof) { printf("Truncated decomp_len.\n"); free(out_buf); fclose(fin); return 1; } if (decomp_len == 0 || global_p + decomp_len > total_original_len) { printf("Invalid decomp_len.\n"); free(out_buf); fclose(fin); return 1; } int huff_flag = br_read_bit(&br); int rep_en_d = br_read_bit(&br); HuffDec hd; memset(&hd, 0, sizeof(hd)); if (huff_flag == 1) { uint8_t lens[256]; memset(lens, 0, sizeof(lens)); for (int s = 0; s < 256; s++) lens[s] = (uint8_t)br_read_bit(&br); // agora lens[] e mascara; le 4 bits para os presentes uint8_t ll[256]; memset(ll, 0, sizeof(ll)); for (int s = 0; s < 256; s++) if (lens[s]) { ll[s] = (uint8_t)br_read_bits(&br, 4); if (br.eof || ll[s] == 0 || ll[s] > 15) { printf("Invalid huff lens.\n"); free(out_buf); fclose(fin); return 1; } } huffdec_build(&hd, ll); if (hd.nsyms == 0) { printf("Empty huffman table.\n"); free(out_buf); fclose(fin); return 1; } } uint64_t block_end_p = global_p + decomp_len; int rhd[3] = {1,8,32}; while (global_p < block_end_p) { int flag = br_read_bit(&br); if (br.eof) { printf("Truncated token.\n"); free(out_buf); fclose(fin); return 1; } if (flag == 0) { if (huff_flag == 1) { int sym = huffdec_decode(&br, &hd); if (sym < 0) { printf("Huffman decode fault.\n"); free(out_buf); fclose(fin); return 1; } out_buf[global_p++] = (uint8_t)sym; } else { out_buf[global_p++] = (uint8_t)br_read_bits(&br, 8); } } else { int use_large = 0; int is_rep_d = 0; int repcode_d = -1; if (rep_en_d) { int rf = br_read_bit(&br); if (rf == 0) { is_rep_d = 1; repcode_d = (int)br_read_bits(&br, 2); } else { use_large = br_read_bit(&br); } } else { use_large = br_read_bit(&br); } int bits_offset = use_large ? 14 : 9; uint32_t len_code = br_read_bits(&br, 3); uint32_t offset = 0; if (is_rep_d) { offset = (uint32_t)rhd[repcode_d]; } else { offset = br_read_bits(&br, bits_offset); } if (br.eof) { printf("Truncated match.\n"); free(out_buf); fclose(fin); return 1; } if (offset == 0 || offset > global_p || offset > (uint32_t)(WS_LARGE + 1)) { printf("CRITICAL DECOMPRESSION GUARD: Pointer underflow blocked!\n"); free(out_buf); fclose(fin); return 1; } uint32_t ml = (len_code < 7) ? (len_code + 3) : (br_read_bits(&br, 8) + 3); if (ml < MIN_MATCH || ml > MAX_MATCH) { printf("Invalid match length.\n"); free(out_buf); fclose(fin); return 1; } if (global_p + ml > block_end_p) { printf("Match overruns block.\n"); free(out_buf); fclose(fin); return 1; } if (is_rep_d) { if(repcode_d>0){int tmp=rhd[repcode_d]; for(int r=repcode_d;r>0;r--) rhd[r]=rhd[r-1]; rhd[0]=tmp;} } else { rhd[2]=rhd[1]; rhd[1]=rhd[0]; rhd[0]=(int)offset; } uint64_t start_pos = global_p - offset; for (uint32_t i = 0; i < ml; i++) { out_buf[global_p] = out_buf[start_pos + i]; global_p++; } } } // sem br_align aqui: proximo header comeca imediatamente (bitstream continuo) } else { // bloco stored: alinha e le bytes crus br_align(&br); uint32_t raw_len = 0; if (fread(&raw_len, sizeof(uint32_t), 1, fin) != 1) { printf("Truncated stored header.\n"); free(out_buf); fclose(fin); return 1; } if (raw_len == 0 || global_p + raw_len > total_original_len) { printf("Invalid stored len.\n"); free(out_buf); fclose(fin); return 1; } if (fread(&out_buf[global_p], 1, raw_len, fin) != raw_len) { printf("Truncated stored payload.\n"); free(out_buf); fclose(fin); return 1; } global_p += raw_len; // realinha leitor apos fread (cnt ja 0, mas garante) br_align(&br); } } fclose(fin); uint32_t checksum_calculado = calcular_adler32(out_buf, (size_t)total_original_len); int sucesso = (checksum_original == checksum_calculado) && (global_p == total_original_len); if (sucesso) { FILE *fout = fopen(saida, "wb"); if (!fout) { printf("Output execution crash.\n"); free(out_buf); return 1; } fwrite(out_buf, 1, (size_t)total_original_len, fout); fclose(fout); printf("Decompression Complete. Status: 100%% INTEGRAL (Adler-32 Checked).\n"); } else { printf("CRITICAL VALIDATION ERROR: Output payload is corrupted.\n"); } free(out_buf); return sucesso ? 0 : 2; } int main(int argc, char** argv) { if (argc != 4) { printf("Usage:\n Compress: %s -c \n Decompress: %s -d \n", argv[0], argv[0]); return 1; } if (strcmp(argv[1], "-c") == 0) return executar_compressao(argv[2], argv[3]); if (strcmp(argv[1], "-d") == 0) return executar_descompressao(argv[2], argv[3]); printf("Invalid layout mode.\n"); return 1; }