diff options
Diffstat (limited to 'util/cbfstool/lzma/lzma.c')
-rw-r--r-- | util/cbfstool/lzma/lzma.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/util/cbfstool/lzma/lzma.c b/util/cbfstool/lzma/lzma.c new file mode 100644 index 0000000000..914d8b7da9 --- /dev/null +++ b/util/cbfstool/lzma/lzma.c @@ -0,0 +1,198 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "../common.h" +#include "C/LzmaDec.h" +#include "C/LzmaEnc.h" + +/* Endianness / unaligned memory access handling */ + +#if defined(__x86_64__) || defined(__i386__) +#define LITTLE_ENDIAN_AND_UNALIGNED_ACCESS_OK +#else +#undef LITTLE_ENDIAN_AND_UNALIGNED_ACCESS_OK +#endif + +#define L (uint64_t) + +static inline uint64_t get_64(const void *p) +{ +#ifdef LITTLE_ENDIAN_AND_UNALIGNED_ACCESS_OK + return *(const uint64_t *)p; +#else + const unsigned char *data = (const unsigned char *)p; + return (L data[0]) | (L data[1] << 8) | (L data[2] << 16) | + (L data[3] << 24) | (L data [4] << 32) | (L data[5] << 40) | + (L data[6] << 48) | (L data[7] << 56); +#endif +} + +static void put_64(void *p, uint64_t value) +{ +#ifdef LITTLE_ENDIAN_AND_UNALIGNED_ACCESS_OK + *(uint64_t *) p = value; +#else + unsigned char *data = (unsigned char *)p; + data[0] = value & 0xff; + data[1] = (value >> 8) & 0xff; + data[2] = (value >> 16) & 0xff; + data[3] = (value >> 24) & 0xff; + data[4] = (value >> 32) & 0xff; + data[5] = (value >> 40) & 0xff; + data[6] = (value >> 48) & 0xff; + data[7] = (value >> 56) & 0xff; +#endif +} + +/* Memory Allocation API */ + +static void *SzAlloc(void *unused, size_t size) +{ + return malloc(size); +} + +static void SzFree(void *unused, void *address) +{ + free(address); +} + +static ISzAlloc LZMAalloc = { SzAlloc, SzFree }; + +/* Streaming API */ + +typedef struct vector { + char *p; + size_t pos; + size_t size; +} vector_t; + +static vector_t instream, outstream; + +static SRes Read(void *unused, void *buf, size_t *size) +{ + if ((instream.size - instream.pos) < *size) + *size = instream.size - instream.pos; + memcpy(buf, instream.p + instream.pos, *size); + instream.pos += *size; + return SZ_OK; +} + +static size_t Write(void *unused, const void *buf, size_t size) +{ + if(outstream.size - outstream.pos < size) + size = outstream.size - outstream.pos; + memcpy(outstream.p + outstream.pos, buf, size); + outstream.pos += size; + return size; +} + +static ISeqInStream is = { Read }; +static ISeqOutStream os = { Write }; + +/** + * Compress a buffer with lzma + * Don't copy the result back if it is too large. + * @param in a pointer to the buffer + * @param in_len the length in bytes + * @param out a pointer to a buffer of at least size in_len + * @param out_len a pointer to the compressed length of in + */ + +void do_lzma_compress(char *in, int in_len, char *out, int *out_len) +{ + if (in_len == 0) { + ERROR("LZMA: Input length is zero.\n"); + return; + } + + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.dictSize = in_len; + props.pb = 0; /* PosStateBits, default: 2, range: 0..4 */ + props.lp = 0; /* LiteralPosStateBits, default: 0, range: 0..4 */ + props.lc = 1; /* LiteralContextBits, default: 3, range: 0..8 */ + props.fb = 273; /* NumFastBytes */ + props.mc = 0; /* MatchFinderCycles, default: 0 */ + props.algo = 1; /* AlgorithmNo, apparently, 0 and 1 are valid values. 0 = fast mode */ + props.numThreads = 1; + + switch (props.algo) { + case 0: // quick: HC4 + props.btMode = 0; + props.level = 1; + break; + case 1: // full: BT4 + default: + props.level = 9; + props.btMode = 1; + props.numHashBytes = 4; + break; + } + + CLzmaEncHandle p = LzmaEnc_Create(&LZMAalloc); + + int res = LzmaEnc_SetProps(p, &props); + if (res != SZ_OK) { + ERROR("LZMA: LzmaEnc_SetProps failed.\n"); + return; + } + + unsigned char propsEncoded[LZMA_PROPS_SIZE + 8]; + size_t propsSize = sizeof propsEncoded; + res = LzmaEnc_WriteProperties(p, propsEncoded, &propsSize); + if (res != SZ_OK) { + ERROR("LZMA: LzmaEnc_WriteProperties failed.\n"); + return; + } + + instream.p = in; + instream.size = in_len; + + outstream.p = out; + outstream.size = in_len; + + put_64(propsEncoded + LZMA_PROPS_SIZE, in_len); + Write(&os, propsEncoded, LZMA_PROPS_SIZE+8); + + res = LzmaEnc_Encode(p, &os, &is, 0, &LZMAalloc, &LZMAalloc); + if (res != SZ_OK) { + ERROR("LZMA: LzmaEnc_Encode failed %d.\n", res); + return; + } + + *out_len = outstream.pos; +} + +void do_lzma_uncompress(char *dst, int dst_len, char *src, int src_len) +{ + if (src_len <= LZMA_PROPS_SIZE + 8) { + ERROR("LZMA: Input length is too small.\n"); + return; + } + + uint64_t out_sizemax = get_64(&src[LZMA_PROPS_SIZE]); + + if (out_sizemax > (size_t) dst_len) { + ERROR("Not copying %d bytes to %d-byte buffer!\n", + (unsigned int)out_sizemax, dst_len); + return; + } + + ELzmaStatus status; + + size_t destlen = out_sizemax; + size_t srclen = src_len - (LZMA_PROPS_SIZE + 8); + + int res = LzmaDecode((Byte *) dst, &destlen, + (Byte *) &src[LZMA_PROPS_SIZE + 8], &srclen, + (Byte *) &src[0], LZMA_PROPS_SIZE, + LZMA_FINISH_END, + &status, + &LZMAalloc); + + if (res != SZ_OK) { + ERROR("Error while decompressing.\n"); + return; + } +} |