luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

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  */