Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * compress_lz4.c
4 : * Routines for archivers to write a LZ4 compressed data stream.
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/bin/pg_dump/compress_lz4.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres_fe.h"
15 : #include <unistd.h>
16 :
17 : #include "compress_lz4.h"
18 : #include "pg_backup_utils.h"
19 :
20 : #ifdef USE_LZ4
21 : #include <lz4frame.h>
22 :
23 : /*
24 : * LZ4F_HEADER_SIZE_MAX first appeared in v1.7.5 of the library.
25 : * Redefine it for installations with a lesser version.
26 : */
27 : #ifndef LZ4F_HEADER_SIZE_MAX
28 : #define LZ4F_HEADER_SIZE_MAX 32
29 : #endif
30 :
31 : /*---------------------------------
32 : * Common to both compression APIs
33 : *---------------------------------
34 : */
35 :
36 : /*
37 : * (de)compression state used by both the Compressor and Stream APIs.
38 : */
39 : typedef struct LZ4State
40 : {
41 : /*
42 : * Used by the Stream API to keep track of the file stream.
43 : */
44 : FILE *fp;
45 :
46 : LZ4F_preferences_t prefs;
47 :
48 : LZ4F_compressionContext_t ctx;
49 : LZ4F_decompressionContext_t dtx;
50 :
51 : /*
52 : * Used by the Stream API's lazy initialization.
53 : */
54 : bool inited;
55 :
56 : /*
57 : * Used by the Stream API to distinguish between compression and
58 : * decompression operations.
59 : */
60 : bool compressing;
61 :
62 : /*
63 : * I/O buffer area.
64 : */
65 : char *buffer; /* buffer for compressed data */
66 : size_t buflen; /* allocated size of buffer */
67 : size_t bufdata; /* amount of valid data currently in buffer */
68 : /* These fields are used only while decompressing: */
69 : size_t bufnext; /* next buffer position to decompress */
70 : char *outbuf; /* buffer for decompressed data */
71 : size_t outbuflen; /* allocated size of outbuf */
72 : size_t outbufdata; /* amount of valid data currently in outbuf */
73 : size_t outbufnext; /* next outbuf position to return */
74 :
75 : /*
76 : * Used by both APIs to keep track of error codes.
77 : */
78 : size_t errcode;
79 : } LZ4State;
80 :
81 : /*
82 : * LZ4State_compression_init
83 : * Initialize the required LZ4State members for compression.
84 : *
85 : * Write the LZ4 frame header in a buffer keeping track of its length. Users of
86 : * this function can choose when and how to write the header to a file stream.
87 : *
88 : * Returns true on success. In case of a failure returns false, and stores the
89 : * error code in state->errcode.
90 : */
91 : static bool
92 14 : LZ4State_compression_init(LZ4State *state)
93 : {
94 : size_t status;
95 :
96 : /*
97 : * Compute size needed for buffer, assuming we will present at most
98 : * DEFAULT_IO_BUFFER_SIZE input bytes at a time.
99 : */
100 14 : state->buflen = LZ4F_compressBound(DEFAULT_IO_BUFFER_SIZE, &state->prefs);
101 :
102 : /*
103 : * Add some slop to ensure we're not forced to flush every time.
104 : *
105 : * The present slop factor of 50% is chosen so that the typical output
106 : * block size is about 128K when DEFAULT_IO_BUFFER_SIZE = 128K. We might
107 : * need a different slop factor to maintain that equivalence if
108 : * DEFAULT_IO_BUFFER_SIZE is changed dramatically.
109 : */
110 14 : state->buflen += state->buflen / 2;
111 :
112 : /*
113 : * LZ4F_compressBegin requires a buffer that is greater or equal to
114 : * LZ4F_HEADER_SIZE_MAX. Verify that the requirement is met.
115 : */
116 14 : if (state->buflen < LZ4F_HEADER_SIZE_MAX)
117 0 : state->buflen = LZ4F_HEADER_SIZE_MAX;
118 :
119 14 : status = LZ4F_createCompressionContext(&state->ctx, LZ4F_VERSION);
120 14 : if (LZ4F_isError(status))
121 : {
122 0 : state->errcode = status;
123 0 : return false;
124 : }
125 :
126 14 : state->buffer = pg_malloc(state->buflen);
127 :
128 : /*
129 : * Insert LZ4 header into buffer.
130 : */
131 14 : status = LZ4F_compressBegin(state->ctx,
132 14 : state->buffer, state->buflen,
133 14 : &state->prefs);
134 14 : if (LZ4F_isError(status))
135 : {
136 0 : state->errcode = status;
137 0 : return false;
138 : }
139 :
140 14 : state->bufdata = status;
141 :
142 14 : return true;
143 : }
144 :
145 : /*----------------------
146 : * Compressor API
147 : *----------------------
148 : */
149 :
150 : /* Private routines that support LZ4 compressed data I/O */
151 :
152 : static void
153 6 : ReadDataFromArchiveLZ4(ArchiveHandle *AH, CompressorState *cs)
154 : {
155 : size_t r;
156 : size_t readbuflen;
157 : char *outbuf;
158 : char *readbuf;
159 6 : LZ4F_decompressionContext_t ctx = NULL;
160 : LZ4F_decompressOptions_t dec_opt;
161 : LZ4F_errorCode_t status;
162 :
163 6 : memset(&dec_opt, 0, sizeof(dec_opt));
164 6 : status = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
165 6 : if (LZ4F_isError(status))
166 0 : pg_fatal("could not create LZ4 decompression context: %s",
167 : LZ4F_getErrorName(status));
168 :
169 6 : outbuf = pg_malloc(DEFAULT_IO_BUFFER_SIZE);
170 6 : readbuf = pg_malloc(DEFAULT_IO_BUFFER_SIZE);
171 6 : readbuflen = DEFAULT_IO_BUFFER_SIZE;
172 14 : while ((r = cs->readF(AH, &readbuf, &readbuflen)) > 0)
173 : {
174 : char *readp;
175 : char *readend;
176 :
177 : /* Process one chunk */
178 8 : readp = readbuf;
179 8 : readend = readbuf + r;
180 18 : while (readp < readend)
181 : {
182 10 : size_t out_size = DEFAULT_IO_BUFFER_SIZE;
183 10 : size_t read_size = readend - readp;
184 :
185 10 : status = LZ4F_decompress(ctx, outbuf, &out_size,
186 : readp, &read_size, &dec_opt);
187 10 : if (LZ4F_isError(status))
188 0 : pg_fatal("could not decompress: %s",
189 : LZ4F_getErrorName(status));
190 :
191 10 : ahwrite(outbuf, 1, out_size, AH);
192 10 : readp += read_size;
193 : }
194 : }
195 :
196 6 : pg_free(outbuf);
197 6 : pg_free(readbuf);
198 :
199 6 : status = LZ4F_freeDecompressionContext(ctx);
200 6 : if (LZ4F_isError(status))
201 0 : pg_fatal("could not free LZ4 decompression context: %s",
202 : LZ4F_getErrorName(status));
203 6 : }
204 :
205 : static void
206 8 : WriteDataToArchiveLZ4(ArchiveHandle *AH, CompressorState *cs,
207 : const void *data, size_t dLen)
208 : {
209 8 : LZ4State *state = (LZ4State *) cs->private_data;
210 8 : size_t remaining = dLen;
211 :
212 20 : while (remaining > 0)
213 : {
214 : size_t chunk;
215 : size_t required;
216 : size_t status;
217 :
218 : /* We don't try to present more than DEFAULT_IO_BUFFER_SIZE bytes */
219 12 : chunk = Min(remaining, (size_t) DEFAULT_IO_BUFFER_SIZE);
220 :
221 : /* If not enough space, must flush buffer */
222 12 : required = LZ4F_compressBound(chunk, &state->prefs);
223 12 : if (required > state->buflen - state->bufdata)
224 : {
225 2 : cs->writeF(AH, state->buffer, state->bufdata);
226 2 : state->bufdata = 0;
227 : }
228 :
229 12 : status = LZ4F_compressUpdate(state->ctx,
230 12 : state->buffer + state->bufdata,
231 12 : state->buflen - state->bufdata,
232 : data, chunk, NULL);
233 :
234 12 : if (LZ4F_isError(status))
235 0 : pg_fatal("could not compress data: %s",
236 : LZ4F_getErrorName(status));
237 :
238 12 : state->bufdata += status;
239 :
240 12 : data = ((const char *) data) + chunk;
241 12 : remaining -= chunk;
242 : }
243 8 : }
244 :
245 : static void
246 12 : EndCompressorLZ4(ArchiveHandle *AH, CompressorState *cs)
247 : {
248 12 : LZ4State *state = (LZ4State *) cs->private_data;
249 : size_t required;
250 : size_t status;
251 :
252 : /* Nothing needs to be done */
253 12 : if (!state)
254 6 : return;
255 :
256 : /* We might need to flush the buffer to make room for LZ4F_compressEnd */
257 6 : required = LZ4F_compressBound(0, &state->prefs);
258 6 : if (required > state->buflen - state->bufdata)
259 : {
260 0 : cs->writeF(AH, state->buffer, state->bufdata);
261 0 : state->bufdata = 0;
262 : }
263 :
264 6 : status = LZ4F_compressEnd(state->ctx,
265 6 : state->buffer + state->bufdata,
266 6 : state->buflen - state->bufdata,
267 : NULL);
268 6 : if (LZ4F_isError(status))
269 0 : pg_fatal("could not end compression: %s",
270 : LZ4F_getErrorName(status));
271 6 : state->bufdata += status;
272 :
273 : /* Write the final bufferload */
274 6 : cs->writeF(AH, state->buffer, state->bufdata);
275 :
276 6 : status = LZ4F_freeCompressionContext(state->ctx);
277 6 : if (LZ4F_isError(status))
278 0 : pg_fatal("could not end compression: %s",
279 : LZ4F_getErrorName(status));
280 :
281 6 : pg_free(state->buffer);
282 6 : pg_free(state);
283 :
284 6 : cs->private_data = NULL;
285 : }
286 :
287 : /*
288 : * Public routines that support LZ4 compressed data I/O
289 : */
290 : void
291 12 : InitCompressorLZ4(CompressorState *cs, const pg_compress_specification compression_spec)
292 : {
293 : LZ4State *state;
294 :
295 12 : cs->readData = ReadDataFromArchiveLZ4;
296 12 : cs->writeData = WriteDataToArchiveLZ4;
297 12 : cs->end = EndCompressorLZ4;
298 :
299 12 : cs->compression_spec = compression_spec;
300 :
301 : /*
302 : * Read operations have access to the whole input. No state needs to be
303 : * carried between calls.
304 : */
305 12 : if (cs->readF)
306 6 : return;
307 :
308 6 : state = pg_malloc0(sizeof(*state));
309 6 : if (cs->compression_spec.level >= 0)
310 6 : state->prefs.compressionLevel = cs->compression_spec.level;
311 :
312 6 : if (!LZ4State_compression_init(state))
313 0 : pg_fatal("could not initialize LZ4 compression: %s",
314 : LZ4F_getErrorName(state->errcode));
315 :
316 6 : cs->private_data = state;
317 : }
318 :
319 : /*----------------------
320 : * Compress Stream API
321 : *----------------------
322 : */
323 :
324 :
325 : /*
326 : * LZ4 equivalent to feof() or gzeof(). Return true iff there is no
327 : * more buffered data and the end of the input file has been reached.
328 : */
329 : static bool
330 0 : LZ4Stream_eof(CompressFileHandle *CFH)
331 : {
332 0 : LZ4State *state = (LZ4State *) CFH->private_data;
333 :
334 0 : return state->outbufnext >= state->outbufdata &&
335 0 : state->bufnext >= state->bufdata &&
336 0 : feof(state->fp);
337 : }
338 :
339 : static const char *
340 0 : LZ4Stream_get_error(CompressFileHandle *CFH)
341 : {
342 0 : LZ4State *state = (LZ4State *) CFH->private_data;
343 : const char *errmsg;
344 :
345 0 : if (LZ4F_isError(state->errcode))
346 0 : errmsg = LZ4F_getErrorName(state->errcode);
347 : else
348 0 : errmsg = strerror(errno);
349 :
350 0 : return errmsg;
351 : }
352 :
353 : /*
354 : * Initialize an already alloc'ed LZ4State struct for subsequent calls.
355 : *
356 : * Creates the necessary contexts for either compression or decompression. When
357 : * compressing data (indicated by compressing=true), it additionally writes the
358 : * LZ4 header in the output buffer.
359 : *
360 : * It's expected that a not-yet-initialized LZ4State will be zero-filled.
361 : *
362 : * Returns true on success. In case of a failure returns false, and stores the
363 : * error code in state->errcode.
364 : */
365 : static bool
366 186 : LZ4Stream_init(LZ4State *state, bool compressing)
367 : {
368 : size_t status;
369 :
370 186 : if (state->inited)
371 178 : return true;
372 :
373 8 : state->compressing = compressing;
374 :
375 8 : if (state->compressing)
376 : {
377 8 : if (!LZ4State_compression_init(state))
378 0 : return false;
379 : }
380 : else
381 : {
382 0 : status = LZ4F_createDecompressionContext(&state->dtx, LZ4F_VERSION);
383 0 : if (LZ4F_isError(status))
384 : {
385 0 : state->errcode = status;
386 0 : return false;
387 : }
388 :
389 0 : state->buflen = DEFAULT_IO_BUFFER_SIZE;
390 0 : state->buffer = pg_malloc(state->buflen);
391 0 : state->outbuflen = DEFAULT_IO_BUFFER_SIZE;
392 0 : state->outbuf = pg_malloc(state->outbuflen);
393 : }
394 :
395 8 : state->inited = true;
396 8 : return true;
397 : }
398 :
399 : /*
400 : * The workhorse for reading decompressed content out of an LZ4 compressed
401 : * stream.
402 : *
403 : * It will read up to 'ptrsize' decompressed content, or up to the new line
404 : * char if one is found first when the eol_flag is set.
405 : *
406 : * Returns the number of bytes of decompressed data copied into the ptr
407 : * buffer, or -1 in case of error.
408 : */
409 : static int
410 0 : LZ4Stream_read_internal(LZ4State *state, void *ptr, int ptrsize, bool eol_flag)
411 : {
412 0 : int dsize = 0;
413 0 : int remaining = ptrsize;
414 :
415 : /* Lazy init */
416 0 : if (!LZ4Stream_init(state, false /* decompressing */ ))
417 : {
418 0 : pg_log_error("unable to initialize LZ4 library: %s",
419 : LZ4F_getErrorName(state->errcode));
420 0 : return -1;
421 : }
422 :
423 : /* Loop until postcondition is satisfied */
424 0 : while (remaining > 0)
425 : {
426 : /*
427 : * If we already have some decompressed data, return that.
428 : */
429 0 : if (state->outbufnext < state->outbufdata)
430 : {
431 0 : char *outptr = state->outbuf + state->outbufnext;
432 0 : size_t readlen = state->outbufdata - state->outbufnext;
433 0 : bool eol_found = false;
434 :
435 0 : if (readlen > remaining)
436 0 : readlen = remaining;
437 : /* If eol_flag is set, don't read beyond a newline */
438 0 : if (eol_flag)
439 : {
440 0 : char *eolptr = memchr(outptr, '\n', readlen);
441 :
442 0 : if (eolptr)
443 : {
444 0 : readlen = eolptr - outptr + 1;
445 0 : eol_found = true;
446 : }
447 : }
448 0 : memcpy(ptr, outptr, readlen);
449 0 : ptr = ((char *) ptr) + readlen;
450 0 : state->outbufnext += readlen;
451 0 : dsize += readlen;
452 0 : remaining -= readlen;
453 0 : if (eol_found || remaining == 0)
454 : break;
455 : /* We must have emptied outbuf */
456 : Assert(state->outbufnext >= state->outbufdata);
457 : }
458 :
459 : /*
460 : * If we don't have any pending compressed data, load more into
461 : * state->buffer.
462 : */
463 0 : if (state->bufnext >= state->bufdata)
464 : {
465 : size_t rsize;
466 :
467 0 : rsize = fread(state->buffer, 1, state->buflen, state->fp);
468 0 : if (rsize < state->buflen && !feof(state->fp))
469 : {
470 0 : pg_log_error("could not read from input file: %m");
471 0 : return -1;
472 : }
473 0 : if (rsize == 0)
474 0 : break; /* must be EOF */
475 0 : state->bufdata = rsize;
476 0 : state->bufnext = 0;
477 : }
478 :
479 : /*
480 : * Decompress some data into state->outbuf.
481 : */
482 : {
483 : size_t status;
484 0 : size_t outlen = state->outbuflen;
485 0 : size_t inlen = state->bufdata - state->bufnext;
486 :
487 0 : status = LZ4F_decompress(state->dtx,
488 0 : state->outbuf, &outlen,
489 0 : state->buffer + state->bufnext,
490 : &inlen,
491 : NULL);
492 0 : if (LZ4F_isError(status))
493 : {
494 0 : state->errcode = status;
495 0 : pg_log_error("could not read from input file: %s",
496 : LZ4F_getErrorName(state->errcode));
497 0 : return -1;
498 : }
499 0 : state->bufnext += inlen;
500 0 : state->outbufdata = outlen;
501 0 : state->outbufnext = 0;
502 : }
503 : }
504 :
505 0 : return dsize;
506 : }
507 :
508 : /*
509 : * Compress size bytes from ptr and write them to the stream.
510 : */
511 : static void
512 186 : LZ4Stream_write(const void *ptr, size_t size, CompressFileHandle *CFH)
513 : {
514 186 : LZ4State *state = (LZ4State *) CFH->private_data;
515 186 : size_t remaining = size;
516 :
517 : /* Lazy init */
518 186 : if (!LZ4Stream_init(state, true))
519 0 : pg_fatal("unable to initialize LZ4 library: %s",
520 : LZ4F_getErrorName(state->errcode));
521 :
522 380 : while (remaining > 0)
523 : {
524 : size_t chunk;
525 : size_t required;
526 : size_t status;
527 :
528 : /* We don't try to present more than DEFAULT_IO_BUFFER_SIZE bytes */
529 194 : chunk = Min(remaining, (size_t) DEFAULT_IO_BUFFER_SIZE);
530 :
531 : /* If not enough space, must flush buffer */
532 194 : required = LZ4F_compressBound(chunk, &state->prefs);
533 194 : if (required > state->buflen - state->bufdata)
534 : {
535 4 : errno = 0;
536 4 : if (fwrite(state->buffer, 1, state->bufdata, state->fp) != state->bufdata)
537 : {
538 0 : errno = (errno) ? errno : ENOSPC;
539 0 : pg_fatal("error during writing: %m");
540 : }
541 4 : state->bufdata = 0;
542 : }
543 :
544 194 : status = LZ4F_compressUpdate(state->ctx,
545 194 : state->buffer + state->bufdata,
546 194 : state->buflen - state->bufdata,
547 : ptr, chunk, NULL);
548 194 : if (LZ4F_isError(status))
549 0 : pg_fatal("error during writing: %s", LZ4F_getErrorName(status));
550 194 : state->bufdata += status;
551 :
552 194 : ptr = ((const char *) ptr) + chunk;
553 194 : remaining -= chunk;
554 : }
555 186 : }
556 :
557 : /*
558 : * fread() equivalent implementation for LZ4 compressed files.
559 : */
560 : static size_t
561 0 : LZ4Stream_read(void *ptr, size_t size, CompressFileHandle *CFH)
562 : {
563 0 : LZ4State *state = (LZ4State *) CFH->private_data;
564 : int ret;
565 :
566 0 : if ((ret = LZ4Stream_read_internal(state, ptr, size, false)) < 0)
567 0 : pg_fatal("could not read from input file: %s", LZ4Stream_get_error(CFH));
568 :
569 0 : return (size_t) ret;
570 : }
571 :
572 : /*
573 : * fgetc() equivalent implementation for LZ4 compressed files.
574 : */
575 : static int
576 0 : LZ4Stream_getc(CompressFileHandle *CFH)
577 : {
578 0 : LZ4State *state = (LZ4State *) CFH->private_data;
579 : unsigned char c;
580 :
581 0 : if (LZ4Stream_read_internal(state, &c, 1, false) <= 0)
582 : {
583 0 : if (!LZ4Stream_eof(CFH))
584 0 : pg_fatal("could not read from input file: %s", LZ4Stream_get_error(CFH));
585 : else
586 0 : pg_fatal("could not read from input file: end of file");
587 : }
588 :
589 0 : return c;
590 : }
591 :
592 : /*
593 : * fgets() equivalent implementation for LZ4 compressed files.
594 : */
595 : static char *
596 0 : LZ4Stream_gets(char *ptr, int size, CompressFileHandle *CFH)
597 : {
598 0 : LZ4State *state = (LZ4State *) CFH->private_data;
599 : int ret;
600 :
601 0 : ret = LZ4Stream_read_internal(state, ptr, size - 1, true);
602 :
603 : /*
604 : * LZ4Stream_read_internal returning 0 or -1 means that it was either an
605 : * EOF or an error, but gets_func is defined to return NULL in either case
606 : * so we can treat both the same here.
607 : */
608 0 : if (ret <= 0)
609 0 : return NULL;
610 :
611 : /*
612 : * Our caller expects the return string to be NULL terminated and we know
613 : * that ret is greater than zero.
614 : */
615 0 : ptr[ret - 1] = '\0';
616 :
617 0 : return ptr;
618 : }
619 :
620 : /*
621 : * Finalize (de)compression of a stream. When compressing it will write any
622 : * remaining content and/or generated footer from the LZ4 API.
623 : */
624 : static bool
625 8 : LZ4Stream_close(CompressFileHandle *CFH)
626 : {
627 : FILE *fp;
628 8 : LZ4State *state = (LZ4State *) CFH->private_data;
629 : size_t required;
630 : size_t status;
631 : int ret;
632 :
633 8 : fp = state->fp;
634 8 : if (state->inited)
635 : {
636 8 : if (state->compressing)
637 : {
638 : /* We might need to flush the buffer to make room */
639 8 : required = LZ4F_compressBound(0, &state->prefs);
640 8 : if (required > state->buflen - state->bufdata)
641 : {
642 0 : errno = 0;
643 0 : if (fwrite(state->buffer, 1, state->bufdata, state->fp) != state->bufdata)
644 : {
645 0 : errno = (errno) ? errno : ENOSPC;
646 0 : pg_log_error("could not write to output file: %m");
647 : }
648 0 : state->bufdata = 0;
649 : }
650 :
651 8 : status = LZ4F_compressEnd(state->ctx,
652 8 : state->buffer + state->bufdata,
653 8 : state->buflen - state->bufdata,
654 : NULL);
655 8 : if (LZ4F_isError(status))
656 : {
657 0 : pg_log_error("could not end compression: %s",
658 : LZ4F_getErrorName(status));
659 : }
660 : else
661 8 : state->bufdata += status;
662 :
663 8 : errno = 0;
664 8 : if (fwrite(state->buffer, 1, state->bufdata, state->fp) != state->bufdata)
665 : {
666 0 : errno = (errno) ? errno : ENOSPC;
667 0 : pg_log_error("could not write to output file: %m");
668 : }
669 :
670 8 : status = LZ4F_freeCompressionContext(state->ctx);
671 8 : if (LZ4F_isError(status))
672 0 : pg_log_error("could not end compression: %s",
673 : LZ4F_getErrorName(status));
674 : }
675 : else
676 : {
677 0 : status = LZ4F_freeDecompressionContext(state->dtx);
678 0 : if (LZ4F_isError(status))
679 0 : pg_log_error("could not end decompression: %s",
680 : LZ4F_getErrorName(status));
681 0 : pg_free(state->outbuf);
682 : }
683 :
684 8 : pg_free(state->buffer);
685 : }
686 :
687 8 : pg_free(state);
688 8 : CFH->private_data = NULL;
689 :
690 8 : errno = 0;
691 8 : ret = fclose(fp);
692 8 : if (ret != 0)
693 : {
694 0 : pg_log_error("could not close file: %m");
695 0 : return false;
696 : }
697 :
698 8 : return true;
699 : }
700 :
701 : static bool
702 8 : LZ4Stream_open(const char *path, int fd, const char *mode,
703 : CompressFileHandle *CFH)
704 : {
705 8 : LZ4State *state = (LZ4State *) CFH->private_data;
706 :
707 8 : if (fd >= 0)
708 0 : state->fp = fdopen(dup(fd), mode);
709 : else
710 8 : state->fp = fopen(path, mode);
711 8 : if (state->fp == NULL)
712 : {
713 0 : state->errcode = errno;
714 0 : return false;
715 : }
716 :
717 8 : return true;
718 : }
719 :
720 : static bool
721 6 : LZ4Stream_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
722 : {
723 : char *fname;
724 : int save_errno;
725 : bool ret;
726 :
727 6 : fname = psprintf("%s.lz4", path);
728 6 : ret = CFH->open_func(fname, -1, mode, CFH);
729 :
730 6 : save_errno = errno;
731 6 : pg_free(fname);
732 6 : errno = save_errno;
733 :
734 6 : return ret;
735 : }
736 :
737 : /*
738 : * Public routines
739 : */
740 : void
741 8 : InitCompressFileHandleLZ4(CompressFileHandle *CFH,
742 : const pg_compress_specification compression_spec)
743 : {
744 : LZ4State *state;
745 :
746 8 : CFH->open_func = LZ4Stream_open;
747 8 : CFH->open_write_func = LZ4Stream_open_write;
748 8 : CFH->read_func = LZ4Stream_read;
749 8 : CFH->write_func = LZ4Stream_write;
750 8 : CFH->gets_func = LZ4Stream_gets;
751 8 : CFH->getc_func = LZ4Stream_getc;
752 8 : CFH->eof_func = LZ4Stream_eof;
753 8 : CFH->close_func = LZ4Stream_close;
754 8 : CFH->get_error_func = LZ4Stream_get_error;
755 :
756 8 : CFH->compression_spec = compression_spec;
757 8 : state = pg_malloc0(sizeof(*state));
758 8 : if (CFH->compression_spec.level >= 0)
759 8 : state->prefs.compressionLevel = CFH->compression_spec.level;
760 :
761 8 : CFH->private_data = state;
762 8 : }
763 : #else /* USE_LZ4 */
764 : void
765 : InitCompressorLZ4(CompressorState *cs,
766 : const pg_compress_specification compression_spec)
767 : {
768 : pg_fatal("this build does not support compression with %s", "LZ4");
769 : }
770 :
771 : void
772 : InitCompressFileHandleLZ4(CompressFileHandle *CFH,
773 : const pg_compress_specification compression_spec)
774 : {
775 : pg_fatal("this build does not support compression with %s", "LZ4");
776 : }
777 : #endif /* USE_LZ4 */
|