LZMA.c (8803B)
1 #include "compression.h" 2 #include <stdlib.h> 3 #include <string.h> 4 #include <lauxlib.h> 5 6 /* 7 * LZMA Compression Implementation 8 * 9 * NOTE: This is a SIMPLIFIED implementation for educational purposes. 10 * For production use, integrate one of these proven libraries: 11 * 12 * 1. LZMA SDK (Recommended) ⭐ 13 * - Official implementation 14 * - Download: https://www.7-zip.org/sdk.html 15 * - Files needed: LzmaDec.c, LzmaEnc.c, LzFind.c 16 * - Public domain 17 * 18 * 2. XZ Utils 19 * - Modern LZMA implementation 20 * - Better API than LZMA SDK 21 * - Used by Linux kernel 22 * 23 * This simplified version implements: 24 * - Basic LZMA header structure 25 * - Uncompressed storage (as placeholder) 26 * - For real compression, integrate LZMA SDK 27 */ 28 29 /* LZMA constants */ 30 #define LZMA_PROPS_SIZE 5 31 #define LZMA_HEADER_SIZE 13 // props(5) + uncompressed_size(8) 32 #define LZMA_DICT_SIZE_DEFAULT (1 << 23) // 8MB 33 34 /* LZMA header structure */ 35 typedef struct { 36 uint8_t props[LZMA_PROPS_SIZE]; // LZMA properties 37 uint64_t uncompressed_size; // Size of original data (or -1 if unknown) 38 } lzma_header_t; 39 40 /* Get dictionary size based on compression level (0-9) */ 41 static uint32_t lzma_get_dict_size(int level) { 42 /* Dictionary size increases with level: 43 * Level 0-1: 64KB 44 * Level 2-3: 1MB 45 * Level 4-5: 4MB 46 * Level 6-7: 8MB 47 * Level 8-9: 16MB 48 */ 49 if (level < 0) level = 0; 50 if (level > 9) level = 9; 51 52 if (level <= 1) return 1 << 16; /* 64KB */ 53 else if (level <= 3) return 1 << 20; /* 1MB */ 54 else if (level <= 5) return 1 << 22; /* 4MB */ 55 else if (level <= 7) return 1 << 23; /* 8MB */ 56 else return 1 << 24; /* 16MB */ 57 } 58 59 /* Get LZMA properties based on compression level */ 60 static void lzma_get_props(uint8_t* props, int level) { 61 /* Properties: lc=3, lp=0, pb=2 62 * props[0] = (pb * 5 + lp) * 9 + lc 63 * props[1-4] = dict_size (little-endian) 64 */ 65 props[0] = (2 * 5 + 0) * 9 + 3; // lc=3, lp=0, pb=2 66 67 uint32_t dict_size = lzma_get_dict_size(level); 68 props[1] = dict_size & 0xFF; 69 props[2] = (dict_size >> 8) & 0xFF; 70 props[3] = (dict_size >> 16) & 0xFF; 71 props[4] = (dict_size >> 24) & 0xFF; 72 } 73 74 /* Write 64-bit little-endian */ 75 static void write_le64(uint8_t* buf, uint64_t value) { 76 for (int i = 0; i < 8; i++) { 77 buf[i] = (value >> (i * 8)) & 0xFF; 78 } 79 } 80 81 /* Read 64-bit little-endian */ 82 static uint64_t read_le64(const uint8_t* buf) { 83 uint64_t value = 0; 84 for (int i = 0; i < 8; i++) { 85 value |= ((uint64_t)buf[i]) << (i * 8); 86 } 87 return value; 88 } 89 90 /* Compress using simplified LZMA (uncompressed for now) */ 91 compression_result_t* lzma_compress(const uint8_t* input, uint32_t input_size, int level) { 92 compression_result_t* result = compression_result_create(); 93 if (!result) return NULL; 94 95 /* For this simplified version, we'll store uncompressed data with LZMA header 96 * Format: LZMA props(5) + uncompressed_size(8) + data 97 */ 98 99 uint32_t output_size = LZMA_HEADER_SIZE + input_size; 100 result->data = (uint8_t*)malloc(output_size); 101 if (!result->data) { 102 result->error = COMPRESS_MEM_ERROR; 103 strcpy(result->error_msg, "Memory allocation failed"); 104 return result; 105 } 106 107 uint8_t* out = result->data; 108 uint32_t out_pos = 0; 109 110 /* Write LZMA properties based on compression level */ 111 lzma_get_props(out, level); 112 out_pos += LZMA_PROPS_SIZE; 113 114 /* Write uncompressed size */ 115 write_le64(out + out_pos, input_size); 116 out_pos += 8; 117 118 /* Copy data (uncompressed) */ 119 memcpy(out + out_pos, input, input_size); 120 out_pos += input_size; 121 122 result->size = out_pos; 123 result->error = COMPRESS_OK; 124 125 /* Note: This is a placeholder. For real LZMA compression, integrate LZMA SDK */ 126 return result; 127 } 128 129 /* Decompress LZMA data */ 130 compression_result_t* lzma_decompress(const uint8_t* input, uint32_t input_size) { 131 compression_result_t* result = compression_result_create(); 132 if (!result) return NULL; 133 134 if (input_size < LZMA_HEADER_SIZE) { 135 result->error = COMPRESS_DATA_ERROR; 136 strcpy(result->error_msg, "Input too small for LZMA header"); 137 return result; 138 } 139 140 /* Read LZMA header */ 141 lzma_header_t header; 142 memcpy(header.props, input, LZMA_PROPS_SIZE); 143 header.uncompressed_size = read_le64(input + LZMA_PROPS_SIZE); 144 145 /* Validate properties */ 146 uint8_t d = header.props[0]; 147 if (d >= (9 * 5 * 5)) { 148 result->error = COMPRESS_DATA_ERROR; 149 strcpy(result->error_msg, "Invalid LZMA properties"); 150 return result; 151 } 152 153 /* Decode properties */ 154 uint8_t lc = d % 9; 155 d /= 9; 156 uint8_t pb = d / 5; 157 uint8_t lp = d % 5; 158 159 /* Validate ranges */ 160 if (lc > 8 || lp > 4 || pb > 4) { 161 result->error = COMPRESS_DATA_ERROR; 162 strcpy(result->error_msg, "LZMA properties out of range"); 163 return result; 164 } 165 166 /* Check if uncompressed size is valid */ 167 if (header.uncompressed_size == (uint64_t)-1) { 168 result->error = COMPRESS_ERROR; 169 strcpy(result->error_msg, "Unknown uncompressed size"); 170 return result; 171 } 172 173 /* For simplified version, just copy data (assumes uncompressed) */ 174 uint32_t data_size = input_size - LZMA_HEADER_SIZE; 175 176 if (data_size != header.uncompressed_size) { 177 /* This is compressed data - need LZMA SDK */ 178 result->error = COMPRESS_ERROR; 179 strcpy(result->error_msg, "Real LZMA compression not supported. Integrate LZMA SDK."); 180 return result; 181 } 182 183 /* Allocate output buffer */ 184 result->data = (uint8_t*)malloc(header.uncompressed_size); 185 if (!result->data) { 186 result->error = COMPRESS_MEM_ERROR; 187 strcpy(result->error_msg, "Memory allocation failed"); 188 return result; 189 } 190 191 /* Copy uncompressed data */ 192 memcpy(result->data, input + LZMA_HEADER_SIZE, header.uncompressed_size); 193 result->size = header.uncompressed_size; 194 result->error = COMPRESS_OK; 195 196 return result; 197 } 198 199 /* Lua binding: Compress with LZMA */ 200 int lua_lzma_compress(lua_State* L) { 201 size_t input_size; 202 const uint8_t* input = (const uint8_t*)luaL_checklstring(L, 1, &input_size); 203 int level = luaL_optinteger(L, 2, COMPRESS_LEVEL_DEFAULT); 204 205 compression_result_t* result = lzma_compress(input, input_size, level); 206 207 if (result && result->error == COMPRESS_OK) { 208 lua_pushlstring(L, (const char*)result->data, result->size); 209 compression_result_destroy(result); 210 return 1; 211 } else { 212 lua_pushnil(L); 213 lua_pushstring(L, result ? result->error_msg : "Compression failed"); 214 if (result) compression_result_destroy(result); 215 return 2; 216 } 217 } 218 219 /* Lua binding: Decompress LZMA */ 220 int lua_lzma_decompress(lua_State* L) { 221 size_t input_size; 222 const uint8_t* input = (const uint8_t*)luaL_checklstring(L, 1, &input_size); 223 224 compression_result_t* result = lzma_decompress(input, input_size); 225 226 if (result && result->error == COMPRESS_OK) { 227 lua_pushlstring(L, (const char*)result->data, result->size); 228 compression_result_destroy(result); 229 return 1; 230 } else { 231 lua_pushnil(L); 232 lua_pushstring(L, result ? result->error_msg : "Decompression failed"); 233 if (result) compression_result_destroy(result); 234 return 2; 235 } 236 } 237 238 /* 239 * INTEGRATION GUIDE: 240 * 241 * To add full LZMA support, integrate LZMA SDK: 242 * 243 * 1. Download LZMA SDK from https://www.7-zip.org/sdk.html 244 * 2. Add files to project: compression/LzmaDec.c, compression/LzmaEnc.c, compression/LzFind.c 245 * 3. Include headers: LzmaDec.h, LzmaEnc.h 246 * 247 * 4. Replace lzma_compress() with: 248 * CLzmaEncProps props; 249 * LzmaEncProps_Init(&props); 250 * props.level = level; 251 * props.dictSize = 1 << 23; // 8MB 252 * 253 * size_t propsSize = LZMA_PROPS_SIZE; 254 * size_t destLen = input_size + input_size / 3 + 128; 255 * uint8_t* dest = malloc(destLen); 256 * 257 * int res = LzmaEncode(dest + LZMA_PROPS_SIZE, &destLen, 258 * input, input_size, 259 * &props, dest, &propsSize, 260 * 0, NULL, &g_Alloc, &g_Alloc); 261 * 262 * 5. Replace lzma_decompress() with: 263 * ELzmaStatus status; 264 * size_t destLen = uncompressed_size; 265 * size_t srcLen = input_size - LZMA_HEADER_SIZE; 266 * uint8_t* dest = malloc(destLen); 267 * 268 * int res = LzmaDecode(dest, &destLen, 269 * input + LZMA_HEADER_SIZE, &srcLen, 270 * props, LZMA_PROPS_SIZE, 271 * LZMA_FINISH_ANY, &status, 272 * &g_Alloc); 273 * 274 * Benefits of LZMA: 275 * - Excellent compression ratio (often better than gzip/bzip2) 276 * - Good decompression speed 277 * - Used by 7-Zip, XZ Utils 278 * - Dictionary size up to 4GB 279 */