Skip to content

Commit be34b1e

Browse files
committed
Add liblzma port
1 parent bb4f1dd commit be34b1e

File tree

8 files changed

+468
-0
lines changed

8 files changed

+468
-0
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,4 @@ a license to everyone to use it as detailed in LICENSE.)
529529
* Max Brunsfeld <maxbrunsfeld@gmail.com>
530530
* Basil Fierz <basil.fierz@hotmail.com>
531531
* Rod Hyde <rod@badlydrawngames.com>
532+
* Miłosz Rachwał <me@milek7.pl>

ChangeLog.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ See docs/process.md for more on how version tagging works.
2020

2121
Current Trunk
2222
-------------
23+
- Add port for liblzma library (`liblzma.a`). (#12990)
2324

2425
2.0.10: 12/04/2020
2526
------------------

embuilder.py

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
'harfbuzz',
6161
'icu',
6262
'libjpeg',
63+
'liblzma',
6364
'libpng',
6465
'ogg',
6566
'regal',
@@ -197,6 +198,8 @@ def main():
197198
build_port('ogg', libname('libogg'))
198199
elif what == 'libjpeg':
199200
build_port('libjpeg', libname('libjpeg'))
201+
elif what == 'liblzma':
202+
build_port('liblzma', libname('liblzma'))
200203
elif what == 'libpng':
201204
build_port('libpng', libname('libpng'))
202205
elif what == 'sdl2':

src/settings.js

+3
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,9 @@ var USE_BZIP2 = 0;
11971197
// 1 = use libjpeg from emscripten-ports
11981198
var USE_LIBJPEG = 0;
11991199

1200+
// 1 = use liblzma from emscripten-ports
1201+
var USE_LIBLZMA = 0;
1202+
12001203
// 1 = use libpng from emscripten-ports
12011204
var USE_LIBPNG = 0;
12021205

tests/test_other.py

+5
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,11 @@ def test_libjpeg(self):
14811481
building.emcc(path_from_root('tests', 'jpeg_test.c'), ['--embed-file', 'screenshot.jpg', '-s', 'USE_LIBJPEG=1'], output_filename='a.out.js')
14821482
self.assertContained('Image is 600 by 450 with 3 components', self.run_js('a.out.js', args=['screenshot.jpg']))
14831483

1484+
def test_liblzma(self):
1485+
shutil.copyfile(path_from_root('tests', 'third_party', 'liblzma', 'liblzma_test.c.xz'), 'liblzma_test.c.xz')
1486+
building.emcc(path_from_root('tests', 'third_party', 'liblzma', 'liblzma_test.c'), ['--embed-file', 'liblzma_test.c.xz', '-s', 'USE_LIBLZMA=1'], output_filename='a.out.js')
1487+
self.assertContained('c3d55d80', self.run_js('a.out.js', args=['liblzma_test.c.xz']))
1488+
14841489
def test_bullet(self):
14851490
building.emcc(path_from_root('tests', 'bullet_hello_world.cpp'), ['-s', 'USE_BULLET=1'], output_filename='a.out.js')
14861491
self.assertContained('BULLET RUNNING', self.run_process(config.JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
+283
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// simple liblzma test program adapted from xz/doc/examples/02_decompress.c
2+
// original author: Lasse Collin
3+
4+
///////////////////////////////////////////////////////////////////////////////
5+
//
6+
/// \file liblzma_test.c
7+
/// \brief Decompress .xz files and output crc32 to stdout
8+
///
9+
/// Usage: ./lzma_test INPUT_FILES...
10+
///
11+
/// Example: ./lzma_test foo.xz
12+
//
13+
// This file has been put into the public domain.
14+
// You can do whatever you want with this file.
15+
//
16+
///////////////////////////////////////////////////////////////////////////////
17+
18+
#include <stdbool.h>
19+
#include <stdlib.h>
20+
#include <stdio.h>
21+
#include <string.h>
22+
#include <errno.h>
23+
#include <lzma.h>
24+
25+
26+
static bool
27+
init_decoder(lzma_stream *strm)
28+
{
29+
// Initialize a .xz decoder. The decoder supports a memory usage limit
30+
// and a set of flags.
31+
//
32+
// The memory usage of the decompressor depends on the settings used
33+
// to compress a .xz file. It can vary from less than a megabyte to
34+
// a few gigabytes, but in practice (at least for now) it rarely
35+
// exceeds 65 MiB because that's how much memory is required to
36+
// decompress files created with "xz -9". Settings requiring more
37+
// memory take extra effort to use and don't (at least for now)
38+
// provide significantly better compression in most cases.
39+
//
40+
// Memory usage limit is useful if it is important that the
41+
// decompressor won't consume gigabytes of memory. The need
42+
// for limiting depends on the application. In this example,
43+
// no memory usage limiting is used. This is done by setting
44+
// the limit to UINT64_MAX.
45+
//
46+
// The .xz format allows concatenating compressed files as is:
47+
//
48+
// echo foo | xz > foobar.xz
49+
// echo bar | xz >> foobar.xz
50+
//
51+
// When decompressing normal standalone .xz files, LZMA_CONCATENATED
52+
// should always be used to support decompression of concatenated
53+
// .xz files. If LZMA_CONCATENATED isn't used, the decoder will stop
54+
// after the first .xz stream. This can be useful when .xz data has
55+
// been embedded inside another file format.
56+
//
57+
// Flags other than LZMA_CONCATENATED are supported too, and can
58+
// be combined with bitwise-or. See lzma/container.h
59+
// (src/liblzma/api/lzma/container.h in the source package or e.g.
60+
// /usr/include/lzma/container.h depending on the install prefix)
61+
// for details.
62+
lzma_ret ret = lzma_stream_decoder(
63+
strm, UINT64_MAX, LZMA_CONCATENATED);
64+
65+
// Return successfully if the initialization went fine.
66+
if (ret == LZMA_OK)
67+
return true;
68+
69+
// Something went wrong. The possible errors are documented in
70+
// lzma/container.h (src/liblzma/api/lzma/container.h in the source
71+
// package or e.g. /usr/include/lzma/container.h depending on the
72+
// install prefix).
73+
//
74+
// Note that LZMA_MEMLIMIT_ERROR is never possible here. If you
75+
// specify a very tiny limit, the error will be delayed until
76+
// the first headers have been parsed by a call to lzma_code().
77+
const char *msg;
78+
switch (ret) {
79+
case LZMA_MEM_ERROR:
80+
msg = "Memory allocation failed";
81+
break;
82+
83+
case LZMA_OPTIONS_ERROR:
84+
msg = "Unsupported decompressor flags";
85+
break;
86+
87+
default:
88+
// This is most likely LZMA_PROG_ERROR indicating a bug in
89+
// this program or in liblzma. It is inconvenient to have a
90+
// separate error message for errors that should be impossible
91+
// to occur, but knowing the error code is important for
92+
// debugging. That's why it is good to print the error code
93+
// at least when there is no good error message to show.
94+
msg = "Unknown error, possibly a bug";
95+
break;
96+
}
97+
98+
fprintf(stderr, "Error initializing the decoder: %s (error code %u)\n",
99+
msg, ret);
100+
return false;
101+
}
102+
103+
104+
static bool
105+
decompress(lzma_stream *strm, const char *inname, FILE *infile, uint32_t *crc)
106+
{
107+
// When LZMA_CONCATENATED flag was used when initializing the decoder,
108+
// we need to tell lzma_code() when there will be no more input.
109+
// This is done by setting action to LZMA_FINISH instead of LZMA_RUN
110+
// in the same way as it is done when encoding.
111+
//
112+
// When LZMA_CONCATENATED isn't used, there is no need to use
113+
// LZMA_FINISH to tell when all the input has been read, but it
114+
// is still OK to use it if you want. When LZMA_CONCATENATED isn't
115+
// used, the decoder will stop after the first .xz stream. In that
116+
// case some unused data may be left in strm->next_in.
117+
lzma_action action = LZMA_RUN;
118+
119+
uint8_t inbuf[BUFSIZ];
120+
uint8_t outbuf[BUFSIZ];
121+
122+
strm->next_in = NULL;
123+
strm->avail_in = 0;
124+
strm->next_out = outbuf;
125+
strm->avail_out = sizeof(outbuf);
126+
127+
while (true) {
128+
if (strm->avail_in == 0 && !feof(infile)) {
129+
strm->next_in = inbuf;
130+
strm->avail_in = fread(inbuf, 1, sizeof(inbuf),
131+
infile);
132+
133+
if (ferror(infile)) {
134+
fprintf(stderr, "%s: Read error: %s\n",
135+
inname, strerror(errno));
136+
return false;
137+
}
138+
139+
// Once the end of the input file has been reached,
140+
// we need to tell lzma_code() that no more input
141+
// will be coming. As said before, this isn't required
142+
// if the LZMA_CONCATENATED flag isn't used when
143+
// initializing the decoder.
144+
if (feof(infile))
145+
action = LZMA_FINISH;
146+
}
147+
148+
lzma_ret ret = lzma_code(strm, action);
149+
150+
if (strm->avail_out == 0 || ret == LZMA_STREAM_END) {
151+
size_t write_size = sizeof(outbuf) - strm->avail_out;
152+
153+
*crc = lzma_crc32(outbuf, write_size, *crc);
154+
155+
strm->next_out = outbuf;
156+
strm->avail_out = sizeof(outbuf);
157+
}
158+
159+
if (ret != LZMA_OK) {
160+
// Once everything has been decoded successfully, the
161+
// return value of lzma_code() will be LZMA_STREAM_END.
162+
//
163+
// It is important to check for LZMA_STREAM_END. Do not
164+
// assume that getting ret != LZMA_OK would mean that
165+
// everything has gone well or that when you aren't
166+
// getting more output it must have successfully
167+
// decoded everything.
168+
if (ret == LZMA_STREAM_END)
169+
return true;
170+
171+
// It's not LZMA_OK nor LZMA_STREAM_END,
172+
// so it must be an error code. See lzma/base.h
173+
// (src/liblzma/api/lzma/base.h in the source package
174+
// or e.g. /usr/include/lzma/base.h depending on the
175+
// install prefix) for the list and documentation of
176+
// possible values. Many values listen in lzma_ret
177+
// enumeration aren't possible in this example, but
178+
// can be made possible by enabling memory usage limit
179+
// or adding flags to the decoder initialization.
180+
const char *msg;
181+
switch (ret) {
182+
case LZMA_MEM_ERROR:
183+
msg = "Memory allocation failed";
184+
break;
185+
186+
case LZMA_FORMAT_ERROR:
187+
// .xz magic bytes weren't found.
188+
msg = "The input is not in the .xz format";
189+
break;
190+
191+
case LZMA_OPTIONS_ERROR:
192+
// For example, the headers specify a filter
193+
// that isn't supported by this liblzma
194+
// version (or it hasn't been enabled when
195+
// building liblzma, but no-one sane does
196+
// that unless building liblzma for an
197+
// embedded system). Upgrading to a newer
198+
// liblzma might help.
199+
//
200+
// Note that it is unlikely that the file has
201+
// accidentally became corrupt if you get this
202+
// error. The integrity of the .xz headers is
203+
// always verified with a CRC32, so
204+
// unintentionally corrupt files can be
205+
// distinguished from unsupported files.
206+
msg = "Unsupported compression options";
207+
break;
208+
209+
case LZMA_DATA_ERROR:
210+
msg = "Compressed file is corrupt";
211+
break;
212+
213+
case LZMA_BUF_ERROR:
214+
// Typically this error means that a valid
215+
// file has got truncated, but it might also
216+
// be a damaged part in the file that makes
217+
// the decoder think the file is truncated.
218+
// If you prefer, you can use the same error
219+
// message for this as for LZMA_DATA_ERROR.
220+
msg = "Compressed file is truncated or "
221+
"otherwise corrupt";
222+
break;
223+
224+
default:
225+
// This is most likely LZMA_PROG_ERROR.
226+
msg = "Unknown error, possibly a bug";
227+
break;
228+
}
229+
230+
fprintf(stderr, "%s: Decoder error: "
231+
"%s (error code %u)\n",
232+
inname, msg, ret);
233+
return false;
234+
}
235+
}
236+
}
237+
238+
239+
extern int
240+
main(int argc, char **argv)
241+
{
242+
if (argc <= 1) {
243+
fprintf(stderr, "Usage: %s FILES...\n", argv[0]);
244+
return EXIT_FAILURE;
245+
}
246+
247+
lzma_stream strm = LZMA_STREAM_INIT;
248+
249+
bool success = true;
250+
251+
uint32_t crc = 0;
252+
253+
// Try to decompress all files.
254+
for (int i = 1; i < argc; ++i) {
255+
if (!init_decoder(&strm)) {
256+
// Decoder initialization failed. There's no point
257+
// to retry it so we need to exit.
258+
success = false;
259+
break;
260+
}
261+
262+
FILE *infile = fopen(argv[i], "rb");
263+
264+
if (infile == NULL) {
265+
fprintf(stderr, "%s: Error opening the "
266+
"input file: %s\n",
267+
argv[i], strerror(errno));
268+
success = false;
269+
} else {
270+
success &= decompress(&strm, argv[i], infile, &crc);
271+
fclose(infile);
272+
}
273+
}
274+
275+
// Free the memory allocated for the decoder. This only needs to be
276+
// done after the last file.
277+
lzma_end(&strm);
278+
279+
printf("%08x\n", crc);
280+
281+
return success ? EXIT_SUCCESS : EXIT_FAILURE;
282+
}
283+
3.22 KB
Binary file not shown.

0 commit comments

Comments
 (0)