LCOV - code coverage report
Current view: top level - src/bin/pg_dump - compress_lz4.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 354 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 17 0
Legend: Lines:     hit not hit

            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-2026, 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            0 : LZ4State_compression_init(LZ4State *state)
      93              : {
      94            0 :         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            0 :         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            0 :         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            0 :         if (state->buflen < LZ4F_HEADER_SIZE_MAX)
     117            0 :                 state->buflen = LZ4F_HEADER_SIZE_MAX;
     118              : 
     119            0 :         status = LZ4F_createCompressionContext(&state->ctx, LZ4F_VERSION);
     120            0 :         if (LZ4F_isError(status))
     121              :         {
     122            0 :                 state->errcode = status;
     123            0 :                 return false;
     124              :         }
     125              : 
     126            0 :         state->buffer = pg_malloc(state->buflen);
     127              : 
     128              :         /*
     129              :          * Insert LZ4 header into buffer.
     130              :          */
     131            0 :         status = LZ4F_compressBegin(state->ctx,
     132            0 :                                                                 state->buffer, state->buflen,
     133            0 :                                                                 &state->prefs);
     134            0 :         if (LZ4F_isError(status))
     135              :         {
     136            0 :                 state->errcode = status;
     137            0 :                 return false;
     138              :         }
     139              : 
     140            0 :         state->bufdata = status;
     141              : 
     142            0 :         return true;
     143            0 : }
     144              : 
     145              : /*----------------------
     146              :  * Compressor API
     147              :  *----------------------
     148              :  */
     149              : 
     150              : /* Private routines that support LZ4 compressed data I/O */
     151              : 
     152              : static void
     153            0 : ReadDataFromArchiveLZ4(ArchiveHandle *AH, CompressorState *cs)
     154              : {
     155            0 :         size_t          r;
     156            0 :         size_t          readbuflen;
     157            0 :         char       *outbuf;
     158            0 :         char       *readbuf;
     159            0 :         LZ4F_decompressionContext_t ctx = NULL;
     160            0 :         LZ4F_decompressOptions_t dec_opt;
     161            0 :         LZ4F_errorCode_t status;
     162              : 
     163            0 :         memset(&dec_opt, 0, sizeof(dec_opt));
     164            0 :         status = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
     165            0 :         if (LZ4F_isError(status))
     166            0 :                 pg_fatal("could not create LZ4 decompression context: %s",
     167              :                                  LZ4F_getErrorName(status));
     168              : 
     169            0 :         outbuf = pg_malloc(DEFAULT_IO_BUFFER_SIZE);
     170            0 :         readbuf = pg_malloc(DEFAULT_IO_BUFFER_SIZE);
     171            0 :         readbuflen = DEFAULT_IO_BUFFER_SIZE;
     172            0 :         while ((r = cs->readF(AH, &readbuf, &readbuflen)) > 0)
     173              :         {
     174            0 :                 char       *readp;
     175            0 :                 char       *readend;
     176              : 
     177              :                 /* Process one chunk */
     178            0 :                 readp = readbuf;
     179            0 :                 readend = readbuf + r;
     180            0 :                 while (readp < readend)
     181              :                 {
     182            0 :                         size_t          out_size = DEFAULT_IO_BUFFER_SIZE;
     183            0 :                         size_t          read_size = readend - readp;
     184              : 
     185            0 :                         status = LZ4F_decompress(ctx, outbuf, &out_size,
     186            0 :                                                                          readp, &read_size, &dec_opt);
     187            0 :                         if (LZ4F_isError(status))
     188            0 :                                 pg_fatal("could not decompress: %s",
     189              :                                                  LZ4F_getErrorName(status));
     190              : 
     191            0 :                         ahwrite(outbuf, 1, out_size, AH);
     192            0 :                         readp += read_size;
     193            0 :                 }
     194            0 :         }
     195              : 
     196            0 :         pg_free(outbuf);
     197            0 :         pg_free(readbuf);
     198              : 
     199            0 :         status = LZ4F_freeDecompressionContext(ctx);
     200            0 :         if (LZ4F_isError(status))
     201            0 :                 pg_fatal("could not free LZ4 decompression context: %s",
     202              :                                  LZ4F_getErrorName(status));
     203            0 : }
     204              : 
     205              : static void
     206            0 : WriteDataToArchiveLZ4(ArchiveHandle *AH, CompressorState *cs,
     207              :                                           const void *data, size_t dLen)
     208              : {
     209            0 :         LZ4State   *state = (LZ4State *) cs->private_data;
     210            0 :         size_t          remaining = dLen;
     211              : 
     212            0 :         while (remaining > 0)
     213              :         {
     214            0 :                 size_t          chunk;
     215            0 :                 size_t          required;
     216            0 :                 size_t          status;
     217              : 
     218              :                 /* We don't try to present more than DEFAULT_IO_BUFFER_SIZE bytes */
     219            0 :                 chunk = Min(remaining, (size_t) DEFAULT_IO_BUFFER_SIZE);
     220              : 
     221              :                 /* If not enough space, must flush buffer */
     222            0 :                 required = LZ4F_compressBound(chunk, &state->prefs);
     223            0 :                 if (required > state->buflen - state->bufdata)
     224              :                 {
     225            0 :                         cs->writeF(AH, state->buffer, state->bufdata);
     226            0 :                         state->bufdata = 0;
     227            0 :                 }
     228              : 
     229            0 :                 status = LZ4F_compressUpdate(state->ctx,
     230            0 :                                                                          state->buffer + state->bufdata,
     231            0 :                                                                          state->buflen - state->bufdata,
     232            0 :                                                                          data, chunk, NULL);
     233              : 
     234            0 :                 if (LZ4F_isError(status))
     235            0 :                         pg_fatal("could not compress data: %s",
     236              :                                          LZ4F_getErrorName(status));
     237              : 
     238            0 :                 state->bufdata += status;
     239              : 
     240            0 :                 data = ((const char *) data) + chunk;
     241            0 :                 remaining -= chunk;
     242            0 :         }
     243            0 : }
     244              : 
     245              : static void
     246            0 : EndCompressorLZ4(ArchiveHandle *AH, CompressorState *cs)
     247              : {
     248            0 :         LZ4State   *state = (LZ4State *) cs->private_data;
     249            0 :         size_t          required;
     250            0 :         size_t          status;
     251              : 
     252              :         /* Nothing needs to be done */
     253            0 :         if (!state)
     254            0 :                 return;
     255              : 
     256              :         /* We might need to flush the buffer to make room for LZ4F_compressEnd */
     257            0 :         required = LZ4F_compressBound(0, &state->prefs);
     258            0 :         if (required > state->buflen - state->bufdata)
     259              :         {
     260            0 :                 cs->writeF(AH, state->buffer, state->bufdata);
     261            0 :                 state->bufdata = 0;
     262            0 :         }
     263              : 
     264            0 :         status = LZ4F_compressEnd(state->ctx,
     265            0 :                                                           state->buffer + state->bufdata,
     266            0 :                                                           state->buflen - state->bufdata,
     267              :                                                           NULL);
     268            0 :         if (LZ4F_isError(status))
     269            0 :                 pg_fatal("could not end compression: %s",
     270              :                                  LZ4F_getErrorName(status));
     271            0 :         state->bufdata += status;
     272              : 
     273              :         /* Write the final bufferload */
     274            0 :         cs->writeF(AH, state->buffer, state->bufdata);
     275              : 
     276            0 :         status = LZ4F_freeCompressionContext(state->ctx);
     277            0 :         if (LZ4F_isError(status))
     278            0 :                 pg_fatal("could not end compression: %s",
     279              :                                  LZ4F_getErrorName(status));
     280              : 
     281            0 :         pg_free(state->buffer);
     282            0 :         pg_free(state);
     283              : 
     284            0 :         cs->private_data = NULL;
     285            0 : }
     286              : 
     287              : /*
     288              :  * Public routines that support LZ4 compressed data I/O
     289              :  */
     290              : void
     291            0 : InitCompressorLZ4(CompressorState *cs, const pg_compress_specification compression_spec)
     292              : {
     293            0 :         LZ4State   *state;
     294              : 
     295            0 :         cs->readData = ReadDataFromArchiveLZ4;
     296            0 :         cs->writeData = WriteDataToArchiveLZ4;
     297            0 :         cs->end = EndCompressorLZ4;
     298              : 
     299            0 :         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            0 :         if (cs->readF)
     306            0 :                 return;
     307              : 
     308            0 :         state = pg_malloc0(sizeof(*state));
     309            0 :         if (cs->compression_spec.level >= 0)
     310            0 :                 state->prefs.compressionLevel = cs->compression_spec.level;
     311              : 
     312            0 :         if (!LZ4State_compression_init(state))
     313            0 :                 pg_fatal("could not initialize LZ4 compression: %s",
     314              :                                  LZ4F_getErrorName(state->errcode));
     315              : 
     316            0 :         cs->private_data = state;
     317            0 : }
     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            0 : }
     338              : 
     339              : static const char *
     340            0 : LZ4Stream_get_error(CompressFileHandle *CFH)
     341              : {
     342            0 :         LZ4State   *state = (LZ4State *) CFH->private_data;
     343            0 :         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            0 : }
     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            0 : LZ4Stream_init(LZ4State *state, bool compressing)
     367              : {
     368            0 :         size_t          status;
     369              : 
     370            0 :         if (state->inited)
     371            0 :                 return true;
     372              : 
     373            0 :         state->compressing = compressing;
     374              : 
     375            0 :         if (state->compressing)
     376              :         {
     377            0 :                 if (!LZ4State_compression_init(state))
     378            0 :                         return false;
     379            0 :         }
     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            0 :         state->inited = true;
     396            0 :         return true;
     397            0 : }
     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            0 :                                 }
     447            0 :                         }
     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            0 :                                 break;
     455              :                         /* We must have emptied outbuf */
     456            0 :                         Assert(state->outbufnext >= state->outbufdata);
     457            0 :                 }
     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            0 :                         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            0 :                 }
     478              : 
     479              :                 /*
     480              :                  * Decompress some data into state->outbuf.
     481              :                  */
     482              :                 {
     483            0 :                         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            0 :                 }
     503              :         }
     504              : 
     505            0 :         return dsize;
     506            0 : }
     507              : 
     508              : /*
     509              :  * Compress size bytes from ptr and write them to the stream.
     510              :  */
     511              : static void
     512            0 : LZ4Stream_write(const void *ptr, size_t size, CompressFileHandle *CFH)
     513              : {
     514            0 :         LZ4State   *state = (LZ4State *) CFH->private_data;
     515            0 :         size_t          remaining = size;
     516              : 
     517              :         /* Lazy init */
     518            0 :         if (!LZ4Stream_init(state, true))
     519            0 :                 pg_fatal("unable to initialize LZ4 library: %s",
     520              :                                  LZ4F_getErrorName(state->errcode));
     521              : 
     522            0 :         while (remaining > 0)
     523              :         {
     524            0 :                 size_t          chunk;
     525            0 :                 size_t          required;
     526            0 :                 size_t          status;
     527              : 
     528              :                 /* We don't try to present more than DEFAULT_IO_BUFFER_SIZE bytes */
     529            0 :                 chunk = Min(remaining, (size_t) DEFAULT_IO_BUFFER_SIZE);
     530              : 
     531              :                 /* If not enough space, must flush buffer */
     532            0 :                 required = LZ4F_compressBound(chunk, &state->prefs);
     533            0 :                 if (required > state->buflen - state->bufdata)
     534              :                 {
     535            0 :                         errno = 0;
     536            0 :                         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            0 :                         }
     541            0 :                         state->bufdata = 0;
     542            0 :                 }
     543              : 
     544            0 :                 status = LZ4F_compressUpdate(state->ctx,
     545            0 :                                                                          state->buffer + state->bufdata,
     546            0 :                                                                          state->buflen - state->bufdata,
     547            0 :                                                                          ptr, chunk, NULL);
     548            0 :                 if (LZ4F_isError(status))
     549            0 :                         pg_fatal("error during writing: %s", LZ4F_getErrorName(status));
     550            0 :                 state->bufdata += status;
     551              : 
     552            0 :                 ptr = ((const char *) ptr) + chunk;
     553            0 :                 remaining -= chunk;
     554            0 :         }
     555            0 : }
     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            0 :         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            0 : }
     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            0 :         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            0 :         }
     588              : 
     589            0 :         return c;
     590            0 : }
     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            0 :         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            0 : }
     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            0 : LZ4Stream_close(CompressFileHandle *CFH)
     626              : {
     627            0 :         FILE       *fp;
     628            0 :         LZ4State   *state = (LZ4State *) CFH->private_data;
     629            0 :         size_t          required;
     630            0 :         size_t          status;
     631            0 :         int                     ret;
     632              : 
     633            0 :         fp = state->fp;
     634            0 :         if (state->inited)
     635              :         {
     636            0 :                 if (state->compressing)
     637              :                 {
     638              :                         /* We might need to flush the buffer to make room */
     639            0 :                         required = LZ4F_compressBound(0, &state->prefs);
     640            0 :                         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            0 :                                 }
     648            0 :                                 state->bufdata = 0;
     649            0 :                         }
     650              : 
     651            0 :                         status = LZ4F_compressEnd(state->ctx,
     652            0 :                                                                           state->buffer + state->bufdata,
     653            0 :                                                                           state->buflen - state->bufdata,
     654              :                                                                           NULL);
     655            0 :                         if (LZ4F_isError(status))
     656              :                         {
     657            0 :                                 pg_log_error("could not end compression: %s",
     658              :                                                          LZ4F_getErrorName(status));
     659            0 :                         }
     660              :                         else
     661            0 :                                 state->bufdata += status;
     662              : 
     663            0 :                         errno = 0;
     664            0 :                         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            0 :                         }
     669              : 
     670            0 :                         status = LZ4F_freeCompressionContext(state->ctx);
     671            0 :                         if (LZ4F_isError(status))
     672            0 :                                 pg_log_error("could not end compression: %s",
     673              :                                                          LZ4F_getErrorName(status));
     674            0 :                 }
     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            0 :                 pg_free(state->buffer);
     685            0 :         }
     686              : 
     687            0 :         pg_free(state);
     688            0 :         CFH->private_data = NULL;
     689              : 
     690            0 :         errno = 0;
     691            0 :         ret = fclose(fp);
     692            0 :         if (ret != 0)
     693              :         {
     694            0 :                 pg_log_error("could not close file: %m");
     695            0 :                 return false;
     696              :         }
     697              : 
     698            0 :         return true;
     699            0 : }
     700              : 
     701              : static bool
     702            0 : LZ4Stream_open(const char *path, int fd, const char *mode,
     703              :                            CompressFileHandle *CFH)
     704              : {
     705            0 :         LZ4State   *state = (LZ4State *) CFH->private_data;
     706              : 
     707            0 :         if (fd >= 0)
     708            0 :                 state->fp = fdopen(dup(fd), mode);
     709              :         else
     710            0 :                 state->fp = fopen(path, mode);
     711            0 :         if (state->fp == NULL)
     712              :         {
     713            0 :                 state->errcode = errno;
     714            0 :                 return false;
     715              :         }
     716              : 
     717            0 :         return true;
     718            0 : }
     719              : 
     720              : static bool
     721            0 : LZ4Stream_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
     722              : {
     723            0 :         char       *fname;
     724            0 :         int                     save_errno;
     725            0 :         bool            ret;
     726              : 
     727            0 :         fname = psprintf("%s.lz4", path);
     728            0 :         ret = CFH->open_func(fname, -1, mode, CFH);
     729              : 
     730            0 :         save_errno = errno;
     731            0 :         pg_free(fname);
     732            0 :         errno = save_errno;
     733              : 
     734            0 :         return ret;
     735            0 : }
     736              : 
     737              : /*
     738              :  * Public routines
     739              :  */
     740              : void
     741            0 : InitCompressFileHandleLZ4(CompressFileHandle *CFH,
     742              :                                                   const pg_compress_specification compression_spec)
     743              : {
     744            0 :         LZ4State   *state;
     745              : 
     746            0 :         CFH->open_func = LZ4Stream_open;
     747            0 :         CFH->open_write_func = LZ4Stream_open_write;
     748            0 :         CFH->read_func = LZ4Stream_read;
     749            0 :         CFH->write_func = LZ4Stream_write;
     750            0 :         CFH->gets_func = LZ4Stream_gets;
     751            0 :         CFH->getc_func = LZ4Stream_getc;
     752            0 :         CFH->eof_func = LZ4Stream_eof;
     753            0 :         CFH->close_func = LZ4Stream_close;
     754            0 :         CFH->get_error_func = LZ4Stream_get_error;
     755              : 
     756            0 :         CFH->compression_spec = compression_spec;
     757            0 :         state = pg_malloc0(sizeof(*state));
     758            0 :         if (CFH->compression_spec.level >= 0)
     759            0 :                 state->prefs.compressionLevel = CFH->compression_spec.level;
     760              : 
     761            0 :         CFH->private_data = state;
     762            0 : }
     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 */
        

Generated by: LCOV version 2.3.2-1