1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include <lz4/lz4frame.h>
17
18 #define BLOCK_SIZE 65536
19
20 #define WR_NEWFILE O_WRONLY | O_CREAT | O_TRUNC
21 #define PERM_644 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
22
usage(const char * arg0)23 static void usage(const char* arg0) {
24 printf("usage: %s [-1|-9] [-d] <input file> <output file>\n", arg0);
25 printf(" -1 fast compression (default)\n");
26 printf(" -9 high compression (slower)\n");
27 printf(" -d decompress\n");
28 }
29
do_decompress(const char * infile,const char * outfile)30 static int do_decompress(const char* infile, const char* outfile) {
31 int infd, outfd;
32
33 infd = open(infile, O_RDONLY);
34 if (infd < 0) {
35 fprintf(stderr, "could not open %s: %s\n", infile, strerror(errno));
36 return -1;
37 }
38
39 outfd = open(outfile, WR_NEWFILE, PERM_644);
40 if (outfd < 0) {
41 fprintf(stderr, "could not open %s: %s\n", outfile, strerror(errno));
42 close(infd);
43 return -1;
44 }
45
46 LZ4F_decompressionContext_t dctx;
47 LZ4F_errorCode_t errc = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
48 if (LZ4F_isError(errc)) {
49 fprintf(stderr, "could not initialize decompression: %s\n", LZ4F_getErrorName(errc));
50 close(outfd);
51 close(infd);
52 return -1;
53 }
54
55 uint8_t inbuf[BLOCK_SIZE];
56 uint8_t outbuf[BLOCK_SIZE];
57
58 // Read first 4 bytes to let LZ4 tell us how much it expects in the first
59 // pass.
60 size_t src_sz = 4;
61 size_t dst_sz = 0;
62 ssize_t nr = read(infd, inbuf, src_sz);
63 if (nr < (ssize_t)src_sz) {
64 fprintf(stderr, "could not read from %s", infile);
65 if (nr < 0) {
66 fprintf(stderr, ": %s", strerror(errno));
67 }
68 fprintf(stderr, "\n");
69 goto done;
70 }
71 size_t to_read = LZ4F_decompress(dctx, outbuf, &dst_sz, inbuf, &src_sz, NULL);
72 if (LZ4F_isError(to_read)) {
73 fprintf(stderr, "could not decompress %s: %s\n", infile, LZ4F_getErrorName(to_read));
74 goto done;
75 }
76 if (to_read > BLOCK_SIZE) {
77 to_read = BLOCK_SIZE;
78 }
79
80 while ((nr = read(infd, inbuf, to_read)) > 0) {
81 src_sz = nr;
82 ssize_t pos = 0;
83 size_t next = 0;
84
85 while (pos < nr) {
86 dst_sz = BLOCK_SIZE;
87 next = LZ4F_decompress(dctx, outbuf, &dst_sz, inbuf + pos, &src_sz, NULL);
88 if (LZ4F_isError(next)) {
89 fprintf(stderr, "could not decompress %s: %s\n", infile, LZ4F_getErrorName(to_read));
90 goto done;
91 }
92
93 if (dst_sz) {
94 ssize_t nw = write(outfd, outbuf, dst_sz);
95 if (nw != (ssize_t)dst_sz) {
96 fprintf(stderr, "could not write to %s", outfile);
97 if (nw < 0) {
98 fprintf(stderr, ": %s", strerror(errno));
99 }
100 fprintf(stderr, "\n");
101 goto done;
102 }
103 }
104 pos += src_sz;
105 src_sz = nr - pos;
106 }
107
108 to_read = next;
109 if (to_read > BLOCK_SIZE || to_read == 0) {
110 to_read = BLOCK_SIZE;
111 }
112 }
113
114 if (nr < 0) {
115 fprintf(stderr, "error reading %s: %s\n", infile, strerror(errno));
116 goto done;
117 }
118
119 done:
120 LZ4F_freeDecompressionContext(dctx);
121 close(outfd);
122 close(infd);
123 return 0;
124 }
125
do_compress(const char * infile,const char * outfile,int clevel)126 static int do_compress(const char* infile, const char* outfile, int clevel) {
127 int infd, outfd;
128
129 infd = open(infile, O_RDONLY);
130 if (infd < 0) {
131 fprintf(stderr, "could not open %s: %s\n", infile, strerror(errno));
132 return -1;
133 }
134
135 outfd = open(outfile, WR_NEWFILE, PERM_644);
136 if (outfd < 0) {
137 fprintf(stderr, "could not open %s: %s\n", outfile, strerror(errno));
138 close(infd);
139 return -1;
140 }
141
142 LZ4F_preferences_t prefs;
143 memset(&prefs, 0, sizeof(prefs));
144 prefs.compressionLevel = clevel;
145
146 uint8_t inbuf[BLOCK_SIZE];
147 size_t outsize = LZ4F_compressFrameBound(BLOCK_SIZE, &prefs);
148 uint8_t* outbuf = malloc(outsize);
149 if (!outbuf) {
150 fprintf(stderr, "out of memory\n");
151 close(outfd);
152 close(infd);
153 return ENOMEM;
154 }
155
156 // TODO: do the whole file in one frame, using the LZ4F begin/update/end
157 // functions.
158 ssize_t nr = 0;
159 while ((nr = read(infd, inbuf, BLOCK_SIZE)) > 0) {
160 ssize_t csz = LZ4F_compressFrame(outbuf, outsize, inbuf, nr, &prefs);
161 if (LZ4F_isError(csz)) {
162 fprintf(stderr, "error compressing %s: %s\n", infile, LZ4F_getErrorName(csz));
163 goto done;
164 }
165
166 ssize_t nw = write(outfd, outbuf, csz);
167 if (nw != csz) {
168 fprintf(stderr, "could not write to %s", outfile);
169 if (nw < 0) {
170 fprintf(stderr, ": %s", strerror(errno));
171 }
172 fprintf(stderr, "\n");
173 goto done;
174 }
175 }
176
177 if (nr < 0) {
178 fprintf(stderr, "error reading %s: %s\n", infile, strerror(errno));
179 goto done;
180 }
181
182 done:
183 free(outbuf);
184 close(outfd);
185 close(infd);
186 return 0;
187 }
188
main(int argc,char * argv[])189 int main(int argc, char* argv[]) {
190 int clevel = 1;
191 bool decompress = false;
192 const char* infile = NULL;
193 const char* outfile = NULL;
194
195 for (int i = 1; i < argc; i++) {
196 if (!strcmp("-d", argv[i])) {
197 decompress = true;
198 continue;
199 }
200 if (!strcmp("-9", argv[i])) {
201 clevel = 9;
202 continue;
203 }
204 if (!strcmp("-h", argv[i])) {
205 usage(argv[0]);
206 return 0;
207 }
208
209 if (!infile) {
210 infile = argv[i];
211 continue;
212 }
213 if (!outfile) {
214 outfile = argv[i];
215 continue;
216 }
217
218 fprintf(stderr, "Unknown argument: %s\n", argv[i]);
219 usage(argv[0]);
220 return -1;
221 }
222
223 if (!infile || !outfile) {
224 usage(argv[0]);
225 return 0;
226 }
227
228 printf("%scompressing %s into %s",
229 decompress ? "de" : "",
230 infile,
231 outfile);
232 if (!decompress) {
233 printf(" at level %d", clevel);
234 }
235 printf("\n");
236
237 if (decompress) {
238 return do_decompress(infile, outfile);
239 } else {
240 return do_compress(infile, outfile, clevel);
241 }
242 }
243