SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
SDDS_lzma.c
Go to the documentation of this file.
1/**
2 * @file SDDS_lzma.c
3 * @brief Implementation of LZMA-compressed file handling functions.
4 *
5 * This file provides a set of functions to work with files compressed using the LZMA
6 * compression algorithm. It abstracts the complexities of LZMA stream handling, offering
7 * a simple file-like interface for reading from and writing to compressed files.
8 *
9 * Features:
10 * - Open and close LZMA-compressed files.
11 * - Read and write data with automatic compression/decompression.
12 * - Support for reading lines and formatted output.
13 * - Error handling with descriptive messages.
14 *
15 * @copyright
16 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
17 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
18 *
19 * @license
20 * This file is distributed under the terms of the Software License Agreement
21 * found in the file LICENSE included with this distribution.
22 *
23 * @authors
24 * R. Soliday
25 */
26
27
28
29#include <assert.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <stdio.h>
33#include <stdlib.h>
34#if defined(_WIN32)
35//#typedef long_ptr ssize_t
36#else
37# include <syslog.h>
38# include <unistd.h>
39#endif
40#include <sys/stat.h>
41#include <sys/types.h>
42#include <stdint.h>
43#include <string.h>
44#include <stdarg.h>
45#include <lzma.h>
46#include "mdb.h"
47#include "mdb_thread.h"
48#if defined(_WIN32)
49 #define LZMA_BUF_SIZE 40960
50 #include "SDDS.h"
51#endif
52
53#if LZMA_VERSION <= UINT32_C(49990030)
54# define LZMA_EASY_ENCODER(a, b) lzma_easy_encoder_single(a, b)
55#elif LZMA_VERSION <= UINT32_C(49990050)
56# define LZMA_EASY_ENCODER(a, b) lzma_easy_encoder(a, b)
57#else
58# define LZMA_EASY_ENCODER(a, b) lzma_easy_encoder(a, b, LZMA_CHECK_CRC32)
59#endif
60
61#define BUF_SIZE 40960
62
63static const lzma_stream lzma_stream_init = LZMA_STREAM_INIT;
64
65struct lzmafile {
66 lzma_stream str; /* codec stream descriptor */
67 FILE *fp; /* backing file descriptor */
68 char mode; /* access mode ('r' or 'w') */
69 unsigned char rdbuf[BUF_SIZE]; /* read buffer used by lzmaRead */
70};
71
72static MDB_THREAD_LOCK lzmaCompressionLevelLock = MDB_THREAD_LOCK_INITIALIZER;
73static int lzmaCompressionLevel = 2;
74
75static int SDDS_GetLockedLZMACompressionLevel(void) {
76 int level;
77 mdb_thread_lock(&lzmaCompressionLevelLock);
78 level = lzmaCompressionLevel;
79 mdb_thread_unlock(&lzmaCompressionLevelLock);
80 return level;
81}
82
83void SDDS_SetLZMACompressionLevel(int32_t level) {
84 if (level >= 0 && level <= 9) {
85 mdb_thread_lock(&lzmaCompressionLevelLock);
86 lzmaCompressionLevel = level;
87 mdb_thread_unlock(&lzmaCompressionLevelLock);
88 }
89}
90
91int32_t SDDS_GetLZMACompressionLevel(void) {
92 return SDDS_GetLockedLZMACompressionLevel();
93}
94
95/* lzma_open opens the file whose name is the string pointed to
96 by 'path' and associates a stream with it. The 'mode' argument
97 is expected to be 'r' or 'w'. Upon successful completion, a
98 lzmafile pointer will be returned. Upon error, NULL will be returned.*/
99void *lzma_open(const char *path, const char *mode) {
100 int ret;
101
102 /* initialize LZMA stream */
103 struct lzmafile *lf = malloc(sizeof(struct lzmafile));
104 if (!lf)
105 return NULL;
106 lf->fp = fopen(path, mode);
107 if (!lf->fp) {
108 free(lf);
109 return NULL;
110 }
111 lf->str = lzma_stream_init;
112 lf->mode = mode[0];
113 if (mode[0] == 'r') {
114#if LZMA_VERSION <= UINT32_C(49990030)
115 ret = lzma_auto_decoder(&lf->str, NULL, NULL);
116#else
117 ret = lzma_auto_decoder(&lf->str, -1, 0);
118#endif
119 lf->str.avail_in = 0;
120 } else {
121 ret = LZMA_EASY_ENCODER(&lf->str, SDDS_GetLockedLZMACompressionLevel());
122 }
123 if (ret != LZMA_OK) {
124 fprintf(stderr, "lzma_open error: %d\n", ret);
125 lzma_end(&lf->str);
126 fclose(lf->fp);
127 free(lf);
128 return NULL;
129 }
130 return (void *)lf;
131}
132
133/* lzma_close flushes the stream pointed to by the lzmafile pointer
134 and closes the underlying file descriptor. Upon successful
135 completion 0 is returned. On error, EOF is returned. */
136int lzma_close(struct lzmafile *file) {
137 int ret, outsize;
138 unsigned char buf[BUF_SIZE]; /* buffer used when flushing remaining
139 output data in write mode */
140 if (!file)
141 return -1;
142 if (file->mode == 'w') {
143 /* flush LZMA output buffer */
144 for (;;) {
145 file->str.next_out = buf;
146 file->str.avail_out = BUF_SIZE;
147 ret = lzma_code(&file->str, LZMA_FINISH);
148 if (ret != LZMA_STREAM_END && ret != LZMA_OK) {
149 fprintf(stderr, "lzma_close error: encoding failed: %d\n", ret);
150 lzma_end(&file->str);
151 fclose(file->fp);
152 free(file);
153 return EOF;
154 }
155 outsize = BUF_SIZE - file->str.avail_out;
156 if (fwrite(buf, 1, outsize, file->fp) != outsize) {
157 lzma_end(&file->str);
158 fclose(file->fp);
159 free(file);
160 return EOF;
161 }
162 if (ret == LZMA_STREAM_END)
163 break;
164 }
165 }
166 lzma_end(&file->str);
167 ret = fclose(file->fp);
168 free(file);
169 return ret;
170}
171
172/* lzma_read attempts to read up to 'count' bytes from the
173 lzmafile pointer into the buffer 'buf'. On success, the
174 number of bytes is returned. On error, -1 is returned.
175 The 'buf' variable is not terminated by '\0'. */
176long lzma_read(struct lzmafile *file, void *buf, size_t count) {
177 int ret;
178 lzma_stream *lstr;
179 if (file->mode != 'r')
180 return -1;
181
182 lstr = &file->str;
183 lstr->next_out = buf;
184 lstr->avail_out = count;
185
186 /* decompress until EOF or output buffer is full */
187 while (lstr->avail_out) {
188 if (lstr->avail_in == 0) {
189 /* refill input buffer */
190 ret = fread(file->rdbuf, 1, BUF_SIZE, file->fp);
191 if (ret == 0) {
192 break; /* EOF */
193 }
194 lstr->next_in = file->rdbuf; /* buffer containing lzma data just read */
195 lstr->avail_in = ret; /* number of bytes read */
196 }
197 ret = lzma_code(lstr, LZMA_RUN);
198 /* this fills up lstr->next_out and decreases lstr->avail_out */
199 /* it also emptys lstr->next_in and decreases lstr->avail_in */
200 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
201 fprintf(stderr, "lzma_read error: decoding failed: %d\n", ret);
202 return -1;
203 }
204 if (ret == LZMA_STREAM_END) {
205 break; /* EOF */
206 }
207 }
208 return count - lstr->avail_out; /* length of buf that has valid data */
209}
210
211/* lzma_gets reads in at most one less than 'size' characters from
212 the the lzmafile pointer into the buffer pointed to by 's'.
213 Reading stops after an EOF or a newline. If a newline is read
214 it is stored into the buffer. A '\0' is stored after the
215 last character in the buffer. Returns 's' on success and NULL on
216 error. */
217char *lzma_gets(char *s, int size, struct lzmafile *file) {
218 int ret;
219 int i = 0;
220 lzma_stream *lstr;
221 if (file->mode != 'r')
222 return NULL;
223 if (s == NULL || size < 1)
224 return NULL;
225 s[0] = '\0';
226 lstr = &file->str;
227 lstr->next_out = (void *)s;
228
229 /* decompress until newline or EOF or output buffer is full */
230 while (1) {
231 if (lstr->avail_in == 0) {
232 /* refill input buffer */
233 ret = fread(file->rdbuf, 1, BUF_SIZE, file->fp);
234 if (ret == 0) {
235 break; /* EOF */
236 }
237 lstr->next_in = file->rdbuf; /* buffer containing lzma data just read */
238 lstr->avail_in = ret; /* number of bytes read */
239 }
240 if (i + 1 == size) {
241 s[i] = '\0';
242 break;
243 }
244 lstr->avail_out = 1;
245 ret = lzma_code(lstr, LZMA_RUN);
246 /* this fills up lstr->next_out and decreases lstr->avail_out */
247 /* it also emptys lstr->next_in and decreases lstr->avail_in */
248 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
249 fprintf(stderr, "lzma_gets error: decoding failed: %d\n", ret);
250 return NULL;
251 }
252 if (ret == LZMA_STREAM_END) { /* EOF */
253 s[i + 1] = '\0';
254 break;
255 }
256 if (s[i] == 10) { /* 10 is the value for \n */
257 if (i > 0) { /* we sometimes get \10\10 */
258 if ((i == 1) && (s[0] == 32)) {
259 /* when uncompressing the lzma stream we some times end
260 up with \10\32\10 instead of a simple \10 */
261 } else {
262 s[i + 1] = '\0';
263 break;
264 }
265 }
266 }
267 i++;
268 }
269 return s;
270}
271
272/* lzma_write writes up to 'count' bytes from the buffer 'buf'
273 to the file referred to by the lzmafile pointer. On success,
274 the number of bytes written is returned. On error, -1 is returned. */
275long lzma_write(struct lzmafile *file, const void *buf, size_t count) {
276 int ret;
277 lzma_stream *lstr = &file->str;
278 unsigned char *bufout; /* compressed output buffer */
279 bufout = malloc(sizeof(char) * count);
280
281 if (file->mode != 'w') {
282 fprintf(stderr, "lzma_write error: file was not opened for writting\n");
283 free(bufout);
284 return -1;
285 }
286 lstr->next_in = buf;
287 lstr->avail_in = count;
288 while (lstr->avail_in) {
289 lstr->next_out = bufout;
290 lstr->avail_out = count;
291 ret = lzma_code(lstr, LZMA_RUN);
292 if (ret != LZMA_OK) {
293 fprintf(stderr, "lzma_write error: encoding failed: %d\n", ret);
294 free(bufout);
295 return -1;
296 }
297 ret = fwrite(bufout, 1, count - lstr->avail_out, file->fp);
298 if (ret != count - lstr->avail_out) {
299 fprintf(stderr, "lzma_write error\n");
300 free(bufout);
301 return -1;
302 }
303 }
304 free(bufout);
305 return count;
306}
307
308/* lzma_puts writes the string 's' to the lzmafile file pointer,
309 without its trailing '\0'. Returns a non-negative number on
310 success, or EOF on error. */
311int lzma_puts(const char *s, struct lzmafile *file) {
312 int ret;
313 lzma_stream *lstr = &file->str;
314 int count;
315 unsigned char *bufout; /* compressed output buffer */
316 char *buf;
317
318 if (file->mode != 'w') {
319 fprintf(stderr, "lzma_puts error: file was not opened for writting\n");
320 return EOF;
321 }
322 count = strlen(s);
323 bufout = malloc(sizeof(unsigned char) * count);
324 buf = malloc(sizeof(char) * count);
325 //strncpy(buf, s, count);
326 memcpy(buf, s, count);
327
328 lstr->next_in = (void *)buf;
329 lstr->avail_in = count;
330 while (lstr->avail_in) {
331 lstr->next_out = bufout;
332 lstr->avail_out = count;
333 ret = lzma_code(lstr, LZMA_RUN);
334 if (ret != LZMA_OK) {
335 fprintf(stderr, "lzma_puts error: encoding failed: %d\n", ret);
336 free(bufout);
337 free(buf);
338 return EOF;
339 }
340 ret = fwrite(bufout, 1, count - lstr->avail_out, file->fp);
341 if (ret != count - lstr->avail_out) {
342 fprintf(stderr, "lzma_puts error\n");
343 free(bufout);
344 free(buf);
345 return EOF;
346 }
347 }
348 free(bufout);
349 free(buf);
350 return count;
351}
352
353/* lzma_putc writes the character 'c', cast to an unsigned char,
354 to the lzmafile file pointer. Returns the character written as
355 an unsigned char cast to an int or EOF on error. */
356int lzma_putc(int c, struct lzmafile *file) {
357 int ret;
358 lzma_stream *lstr = &file->str;
359
360 unsigned char bufout[1]; /* compressed output buffer */
361 char buf[1];
362
363 if (file->mode != 'w') {
364 fprintf(stderr, "lzma_putc error: file was not opened for writting\n");
365 return EOF;
366 }
367 buf[0] = c;
368
369 lstr->next_in = (void *)buf;
370 lstr->avail_in = 1;
371 while (lstr->avail_in) {
372 lstr->next_out = bufout;
373 lstr->avail_out = 1;
374 ret = lzma_code(lstr, LZMA_RUN);
375 if (ret != LZMA_OK) {
376 fprintf(stderr, "lzma_putc error: encoding failed: %d\n", ret);
377 return EOF;
378 }
379 ret = fwrite(bufout, 1, 1 - lstr->avail_out, file->fp);
380 if (ret != 1 - lstr->avail_out) {
381 fprintf(stderr, "lzma_putc error\n");
382 return EOF;
383 }
384 }
385 return (unsigned char)c;
386}
387
388/* lzma_printf writes the output to the given lzmafile file pointer.
389 Upon success, it returns the number of characters printed (not
390 including the trailing '\0'). If an output error is encountered,
391 a negative value is returned. */
392int lzma_printf(struct lzmafile *file, const char *format, ...) {
393 size_t size = 32768;
394 int len;
395 unsigned char in[32768];
396 va_list va;
397 va_start(va, format);
398
399 in[size - 1] = 0;
400 (void)vsnprintf((char *)in, size, format, va);
401 va_end(va);
402 len = strlen((char *)in);
403
404 /* check that printf() results fit in buffer */
405 if (len <= 0 || len >= (int)size || in[size - 1] != 0) {
406 fprintf(stderr, "lzma_printf error: the printf results do not fit in the buffer\n");
407 return -1;
408 }
409 in[len] = '\0';
410 len = lzma_write(file, in, len);
411
412 return len;
413}
414
415int lzma_eof(struct lzmafile *file) {
416 lzma_stream *lstr;
417 lstr = &file->str;
418 if (lstr->avail_in == 0) {
419 return feof(file->fp);
420 } else {
421 return 0;
422 }
423}
424
425/* lzma_tell and lzma_seek will probably have to be
426 changed if they are seriously going to be used.
427 As far as I know they will only be called for updating
428 an existing file or when using fixed_row_count.
429 Both of which are not allowed when using lzma compression */
430long lzma_tell(struct lzmafile *file) {
431 return ftell(file->fp);
432}
433
434int lzma_seek(struct lzmafile *file, long offset, int whence) {
435 return fseek(file->fp, offset, whence);
436}
437
438void *UnpackLZMAOpen(char *filename) {
439 if (!filename)
440 return NULL;
441 return lzma_open(filename, "rb");
442}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.