Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 1 | #include <stdio.h> |
| 2 | #include <stdint.h> |
| 3 | #include <stdlib.h> |
| 4 | #include <string.h> |
| 5 | #include "../common.h" |
| 6 | #include "C/LzmaDec.h" |
| 7 | #include "C/LzmaEnc.h" |
| 8 | |
| 9 | /* Endianness / unaligned memory access handling */ |
| 10 | |
| 11 | #if defined(__x86_64__) || defined(__i386__) |
| 12 | #define LITTLE_ENDIAN_AND_UNALIGNED_ACCESS_OK |
| 13 | #else |
| 14 | #undef LITTLE_ENDIAN_AND_UNALIGNED_ACCESS_OK |
| 15 | #endif |
| 16 | |
| 17 | #define L (uint64_t) |
| 18 | |
| 19 | static inline uint64_t get_64(const void *p) |
| 20 | { |
| 21 | #ifdef LITTLE_ENDIAN_AND_UNALIGNED_ACCESS_OK |
| 22 | return *(const uint64_t *)p; |
| 23 | #else |
| 24 | const unsigned char *data = (const unsigned char *)p; |
| 25 | return (L data[0]) | (L data[1] << 8) | (L data[2] << 16) | |
| 26 | (L data[3] << 24) | (L data [4] << 32) | (L data[5] << 40) | |
| 27 | (L data[6] << 48) | (L data[7] << 56); |
| 28 | #endif |
| 29 | } |
| 30 | |
| 31 | static void put_64(void *p, uint64_t value) |
| 32 | { |
| 33 | #ifdef LITTLE_ENDIAN_AND_UNALIGNED_ACCESS_OK |
| 34 | *(uint64_t *) p = value; |
| 35 | #else |
| 36 | unsigned char *data = (unsigned char *)p; |
| 37 | data[0] = value & 0xff; |
| 38 | data[1] = (value >> 8) & 0xff; |
| 39 | data[2] = (value >> 16) & 0xff; |
| 40 | data[3] = (value >> 24) & 0xff; |
| 41 | data[4] = (value >> 32) & 0xff; |
| 42 | data[5] = (value >> 40) & 0xff; |
| 43 | data[6] = (value >> 48) & 0xff; |
| 44 | data[7] = (value >> 56) & 0xff; |
| 45 | #endif |
| 46 | } |
| 47 | |
| 48 | /* Memory Allocation API */ |
| 49 | |
| 50 | static void *SzAlloc(void *unused, size_t size) |
| 51 | { |
| 52 | return malloc(size); |
| 53 | } |
| 54 | |
| 55 | static void SzFree(void *unused, void *address) |
| 56 | { |
| 57 | free(address); |
| 58 | } |
| 59 | |
Alexandru Gagniuc | 9ad52fe | 2014-01-27 20:57:54 -0600 | [diff] [blame^] | 60 | static struct ISzAlloc LZMAalloc = { SzAlloc, SzFree }; |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 61 | |
| 62 | /* Streaming API */ |
| 63 | |
Alexandru Gagniuc | 9ad52fe | 2014-01-27 20:57:54 -0600 | [diff] [blame^] | 64 | struct vector_t { |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 65 | char *p; |
| 66 | size_t pos; |
| 67 | size_t size; |
Alexandru Gagniuc | 9ad52fe | 2014-01-27 20:57:54 -0600 | [diff] [blame^] | 68 | }; |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 69 | |
Alexandru Gagniuc | 9ad52fe | 2014-01-27 20:57:54 -0600 | [diff] [blame^] | 70 | static struct vector_t instream, outstream; |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 71 | |
| 72 | static SRes Read(void *unused, void *buf, size_t *size) |
| 73 | { |
| 74 | if ((instream.size - instream.pos) < *size) |
| 75 | *size = instream.size - instream.pos; |
| 76 | memcpy(buf, instream.p + instream.pos, *size); |
| 77 | instream.pos += *size; |
| 78 | return SZ_OK; |
| 79 | } |
| 80 | |
| 81 | static size_t Write(void *unused, const void *buf, size_t size) |
| 82 | { |
| 83 | if(outstream.size - outstream.pos < size) |
| 84 | size = outstream.size - outstream.pos; |
| 85 | memcpy(outstream.p + outstream.pos, buf, size); |
| 86 | outstream.pos += size; |
| 87 | return size; |
| 88 | } |
| 89 | |
Alexandru Gagniuc | 9ad52fe | 2014-01-27 20:57:54 -0600 | [diff] [blame^] | 90 | static struct ISeqInStream is = { Read }; |
| 91 | static struct ISeqOutStream os = { Write }; |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 92 | |
| 93 | /** |
| 94 | * Compress a buffer with lzma |
| 95 | * Don't copy the result back if it is too large. |
| 96 | * @param in a pointer to the buffer |
| 97 | * @param in_len the length in bytes |
| 98 | * @param out a pointer to a buffer of at least size in_len |
| 99 | * @param out_len a pointer to the compressed length of in |
| 100 | */ |
| 101 | |
| 102 | void do_lzma_compress(char *in, int in_len, char *out, int *out_len) |
| 103 | { |
| 104 | if (in_len == 0) { |
| 105 | ERROR("LZMA: Input length is zero.\n"); |
| 106 | return; |
| 107 | } |
| 108 | |
Alexandru Gagniuc | 9ad52fe | 2014-01-27 20:57:54 -0600 | [diff] [blame^] | 109 | struct CLzmaEncProps props; |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 110 | LzmaEncProps_Init(&props); |
| 111 | props.dictSize = in_len; |
| 112 | props.pb = 0; /* PosStateBits, default: 2, range: 0..4 */ |
| 113 | props.lp = 0; /* LiteralPosStateBits, default: 0, range: 0..4 */ |
| 114 | props.lc = 1; /* LiteralContextBits, default: 3, range: 0..8 */ |
| 115 | props.fb = 273; /* NumFastBytes */ |
| 116 | props.mc = 0; /* MatchFinderCycles, default: 0 */ |
| 117 | props.algo = 1; /* AlgorithmNo, apparently, 0 and 1 are valid values. 0 = fast mode */ |
| 118 | props.numThreads = 1; |
| 119 | |
| 120 | switch (props.algo) { |
| 121 | case 0: // quick: HC4 |
| 122 | props.btMode = 0; |
| 123 | props.level = 1; |
| 124 | break; |
| 125 | case 1: // full: BT4 |
| 126 | default: |
| 127 | props.level = 9; |
| 128 | props.btMode = 1; |
| 129 | props.numHashBytes = 4; |
| 130 | break; |
| 131 | } |
| 132 | |
| 133 | CLzmaEncHandle p = LzmaEnc_Create(&LZMAalloc); |
| 134 | |
| 135 | int res = LzmaEnc_SetProps(p, &props); |
| 136 | if (res != SZ_OK) { |
| 137 | ERROR("LZMA: LzmaEnc_SetProps failed.\n"); |
| 138 | return; |
| 139 | } |
| 140 | |
| 141 | unsigned char propsEncoded[LZMA_PROPS_SIZE + 8]; |
| 142 | size_t propsSize = sizeof propsEncoded; |
| 143 | res = LzmaEnc_WriteProperties(p, propsEncoded, &propsSize); |
| 144 | if (res != SZ_OK) { |
| 145 | ERROR("LZMA: LzmaEnc_WriteProperties failed.\n"); |
| 146 | return; |
| 147 | } |
| 148 | |
| 149 | instream.p = in; |
Stefan Reinauer | 33e83ca | 2013-04-08 11:20:55 -0700 | [diff] [blame] | 150 | instream.pos = 0; |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 151 | instream.size = in_len; |
| 152 | |
| 153 | outstream.p = out; |
Stefan Reinauer | 33e83ca | 2013-04-08 11:20:55 -0700 | [diff] [blame] | 154 | outstream.pos = 0; |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 155 | outstream.size = in_len; |
| 156 | |
| 157 | put_64(propsEncoded + LZMA_PROPS_SIZE, in_len); |
| 158 | Write(&os, propsEncoded, LZMA_PROPS_SIZE+8); |
| 159 | |
| 160 | res = LzmaEnc_Encode(p, &os, &is, 0, &LZMAalloc, &LZMAalloc); |
| 161 | if (res != SZ_OK) { |
| 162 | ERROR("LZMA: LzmaEnc_Encode failed %d.\n", res); |
| 163 | return; |
| 164 | } |
| 165 | |
| 166 | *out_len = outstream.pos; |
| 167 | } |
| 168 | |
| 169 | void do_lzma_uncompress(char *dst, int dst_len, char *src, int src_len) |
| 170 | { |
| 171 | if (src_len <= LZMA_PROPS_SIZE + 8) { |
| 172 | ERROR("LZMA: Input length is too small.\n"); |
| 173 | return; |
| 174 | } |
| 175 | |
| 176 | uint64_t out_sizemax = get_64(&src[LZMA_PROPS_SIZE]); |
| 177 | |
| 178 | if (out_sizemax > (size_t) dst_len) { |
| 179 | ERROR("Not copying %d bytes to %d-byte buffer!\n", |
| 180 | (unsigned int)out_sizemax, dst_len); |
| 181 | return; |
| 182 | } |
| 183 | |
Alexandru Gagniuc | 9ad52fe | 2014-01-27 20:57:54 -0600 | [diff] [blame^] | 184 | enum ELzmaStatus status; |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 185 | |
| 186 | size_t destlen = out_sizemax; |
| 187 | size_t srclen = src_len - (LZMA_PROPS_SIZE + 8); |
| 188 | |
Alexandru Gagniuc | 91e9f27 | 2014-01-26 22:55:01 -0600 | [diff] [blame] | 189 | int res = LzmaDecode((uint8_t *) dst, &destlen, |
| 190 | (uint8_t *) &src[LZMA_PROPS_SIZE + 8], &srclen, |
| 191 | (uint8_t *) &src[0], LZMA_PROPS_SIZE, |
Stefan Reinauer | aa3f7ba | 2013-03-28 16:51:45 -0700 | [diff] [blame] | 192 | LZMA_FINISH_END, |
| 193 | &status, |
| 194 | &LZMAalloc); |
| 195 | |
| 196 | if (res != SZ_OK) { |
| 197 | ERROR("Error while decompressing.\n"); |
| 198 | return; |
| 199 | } |
| 200 | } |