LCOV - code coverage report
Current view: top level - src/bin/pg_dump - compress_lz4.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 57.1 % 282 161
Test Date: 2026-02-26 16:16:51 Functions: 64.7 % 17 11
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            7 : 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            7 :     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            7 :     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            7 :     if (state->buflen < LZ4F_HEADER_SIZE_MAX)
     117            0 :         state->buflen = LZ4F_HEADER_SIZE_MAX;
     118              : 
     119            7 :     status = LZ4F_createCompressionContext(&state->ctx, LZ4F_VERSION);
     120            7 :     if (LZ4F_isError(status))
     121              :     {
     122            0 :         state->errcode = status;
     123            0 :         return false;
     124              :     }
     125              : 
     126            7 :     state->buffer = pg_malloc(state->buflen);
     127              : 
     128              :     /*
     129              :      * Insert LZ4 header into buffer.
     130              :      */
     131            7 :     status = LZ4F_compressBegin(state->ctx,
     132            7 :                                 state->buffer, state->buflen,
     133            7 :                                 &state->prefs);
     134            7 :     if (LZ4F_isError(status))
     135              :     {
     136            0 :         state->errcode = status;
     137            0 :         return false;
     138              :     }
     139              : 
     140            7 :     state->bufdata = status;
     141              : 
     142            7 :     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            3 : ReadDataFromArchiveLZ4(ArchiveHandle *AH, CompressorState *cs)
     154              : {
     155              :     size_t      r;
     156              :     size_t      readbuflen;
     157              :     char       *outbuf;
     158              :     char       *readbuf;
     159            3 :     LZ4F_decompressionContext_t ctx = NULL;
     160              :     LZ4F_decompressOptions_t dec_opt;
     161              :     LZ4F_errorCode_t status;
     162              : 
     163            3 :     memset(&dec_opt, 0, sizeof(dec_opt));
     164            3 :     status = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
     165            3 :     if (LZ4F_isError(status))
     166            0 :         pg_fatal("could not create LZ4 decompression context: %s",
     167              :                  LZ4F_getErrorName(status));
     168              : 
     169            3 :     outbuf = pg_malloc(DEFAULT_IO_BUFFER_SIZE);
     170            3 :     readbuf = pg_malloc(DEFAULT_IO_BUFFER_SIZE);
     171            3 :     readbuflen = DEFAULT_IO_BUFFER_SIZE;
     172            7 :     while ((r = cs->readF(AH, &readbuf, &readbuflen)) > 0)
     173              :     {
     174              :         char       *readp;
     175              :         char       *readend;
     176              : 
     177              :         /* Process one chunk */
     178            4 :         readp = readbuf;
     179            4 :         readend = readbuf + r;
     180            9 :         while (readp < readend)
     181              :         {
     182            5 :             size_t      out_size = DEFAULT_IO_BUFFER_SIZE;
     183            5 :             size_t      read_size = readend - readp;
     184              : 
     185            5 :             status = LZ4F_decompress(ctx, outbuf, &out_size,
     186              :                                      readp, &read_size, &dec_opt);
     187            5 :             if (LZ4F_isError(status))
     188            0 :                 pg_fatal("could not decompress: %s",
     189              :                          LZ4F_getErrorName(status));
     190              : 
     191            5 :             ahwrite(outbuf, 1, out_size, AH);
     192            5 :             readp += read_size;
     193              :         }
     194              :     }
     195              : 
     196            3 :     pg_free(outbuf);
     197            3 :     pg_free(readbuf);
     198              : 
     199            3 :     status = LZ4F_freeDecompressionContext(ctx);
     200            3 :     if (LZ4F_isError(status))
     201            0 :         pg_fatal("could not free LZ4 decompression context: %s",
     202              :                  LZ4F_getErrorName(status));
     203            3 : }
     204              : 
     205              : static void
     206            4 : WriteDataToArchiveLZ4(ArchiveHandle *AH, CompressorState *cs,
     207              :                       const void *data, size_t dLen)
     208              : {
     209            4 :     LZ4State   *state = (LZ4State *) cs->private_data;
     210            4 :     size_t      remaining = dLen;
     211              : 
     212           10 :     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            6 :         chunk = Min(remaining, (size_t) DEFAULT_IO_BUFFER_SIZE);
     220              : 
     221              :         /* If not enough space, must flush buffer */
     222            6 :         required = LZ4F_compressBound(chunk, &state->prefs);
     223            6 :         if (required > state->buflen - state->bufdata)
     224              :         {
     225            1 :             cs->writeF(AH, state->buffer, state->bufdata);
     226            1 :             state->bufdata = 0;
     227              :         }
     228              : 
     229            6 :         status = LZ4F_compressUpdate(state->ctx,
     230            6 :                                      state->buffer + state->bufdata,
     231            6 :                                      state->buflen - state->bufdata,
     232              :                                      data, chunk, NULL);
     233              : 
     234            6 :         if (LZ4F_isError(status))
     235            0 :             pg_fatal("could not compress data: %s",
     236              :                      LZ4F_getErrorName(status));
     237              : 
     238            6 :         state->bufdata += status;
     239              : 
     240            6 :         data = ((const char *) data) + chunk;
     241            6 :         remaining -= chunk;
     242              :     }
     243            4 : }
     244              : 
     245              : static void
     246            6 : EndCompressorLZ4(ArchiveHandle *AH, CompressorState *cs)
     247              : {
     248            6 :     LZ4State   *state = (LZ4State *) cs->private_data;
     249              :     size_t      required;
     250              :     size_t      status;
     251              : 
     252              :     /* Nothing needs to be done */
     253            6 :     if (!state)
     254            3 :         return;
     255              : 
     256              :     /* We might need to flush the buffer to make room for LZ4F_compressEnd */
     257            3 :     required = LZ4F_compressBound(0, &state->prefs);
     258            3 :     if (required > state->buflen - state->bufdata)
     259              :     {
     260            0 :         cs->writeF(AH, state->buffer, state->bufdata);
     261            0 :         state->bufdata = 0;
     262              :     }
     263              : 
     264            3 :     status = LZ4F_compressEnd(state->ctx,
     265            3 :                               state->buffer + state->bufdata,
     266            3 :                               state->buflen - state->bufdata,
     267              :                               NULL);
     268            3 :     if (LZ4F_isError(status))
     269            0 :         pg_fatal("could not end compression: %s",
     270              :                  LZ4F_getErrorName(status));
     271            3 :     state->bufdata += status;
     272              : 
     273              :     /* Write the final bufferload */
     274            3 :     cs->writeF(AH, state->buffer, state->bufdata);
     275              : 
     276            3 :     status = LZ4F_freeCompressionContext(state->ctx);
     277            3 :     if (LZ4F_isError(status))
     278            0 :         pg_fatal("could not end compression: %s",
     279              :                  LZ4F_getErrorName(status));
     280              : 
     281            3 :     pg_free(state->buffer);
     282            3 :     pg_free(state);
     283              : 
     284            3 :     cs->private_data = NULL;
     285              : }
     286              : 
     287              : /*
     288              :  * Public routines that support LZ4 compressed data I/O
     289              :  */
     290              : void
     291            6 : InitCompressorLZ4(CompressorState *cs, const pg_compress_specification compression_spec)
     292              : {
     293              :     LZ4State   *state;
     294              : 
     295            6 :     cs->readData = ReadDataFromArchiveLZ4;
     296            6 :     cs->writeData = WriteDataToArchiveLZ4;
     297            6 :     cs->end = EndCompressorLZ4;
     298              : 
     299            6 :     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            6 :     if (cs->readF)
     306            3 :         return;
     307              : 
     308            3 :     state = pg_malloc0_object(LZ4State);
     309            3 :     if (cs->compression_spec.level >= 0)
     310            3 :         state->prefs.compressionLevel = cs->compression_spec.level;
     311              : 
     312            3 :     if (!LZ4State_compression_init(state))
     313            0 :         pg_fatal("could not initialize LZ4 compression: %s",
     314              :                  LZ4F_getErrorName(state->errcode));
     315              : 
     316            3 :     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           93 : LZ4Stream_init(LZ4State *state, bool compressing)
     367              : {
     368              :     size_t      status;
     369              : 
     370           93 :     if (state->inited)
     371           89 :         return true;
     372              : 
     373            4 :     state->compressing = compressing;
     374              : 
     375            4 :     if (state->compressing)
     376              :     {
     377            4 :         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            4 :     state->inited = true;
     396            4 :     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           93 : LZ4Stream_write(const void *ptr, size_t size, CompressFileHandle *CFH)
     513              : {
     514           93 :     LZ4State   *state = (LZ4State *) CFH->private_data;
     515           93 :     size_t      remaining = size;
     516              : 
     517              :     /* Lazy init */
     518           93 :     if (!LZ4Stream_init(state, true))
     519            0 :         pg_fatal("unable to initialize LZ4 library: %s",
     520              :                  LZ4F_getErrorName(state->errcode));
     521              : 
     522          190 :     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           97 :         chunk = Min(remaining, (size_t) DEFAULT_IO_BUFFER_SIZE);
     530              : 
     531              :         /* If not enough space, must flush buffer */
     532           97 :         required = LZ4F_compressBound(chunk, &state->prefs);
     533           97 :         if (required > state->buflen - state->bufdata)
     534              :         {
     535            2 :             errno = 0;
     536            2 :             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            2 :             state->bufdata = 0;
     542              :         }
     543              : 
     544           97 :         status = LZ4F_compressUpdate(state->ctx,
     545           97 :                                      state->buffer + state->bufdata,
     546           97 :                                      state->buflen - state->bufdata,
     547              :                                      ptr, chunk, NULL);
     548           97 :         if (LZ4F_isError(status))
     549            0 :             pg_fatal("error during writing: %s", LZ4F_getErrorName(status));
     550           97 :         state->bufdata += status;
     551              : 
     552           97 :         ptr = ((const char *) ptr) + chunk;
     553           97 :         remaining -= chunk;
     554              :     }
     555           93 : }
     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            4 : LZ4Stream_close(CompressFileHandle *CFH)
     626              : {
     627              :     FILE       *fp;
     628            4 :     LZ4State   *state = (LZ4State *) CFH->private_data;
     629              :     size_t      required;
     630              :     size_t      status;
     631              :     int         ret;
     632              : 
     633            4 :     fp = state->fp;
     634            4 :     if (state->inited)
     635              :     {
     636            4 :         if (state->compressing)
     637              :         {
     638              :             /* We might need to flush the buffer to make room */
     639            4 :             required = LZ4F_compressBound(0, &state->prefs);
     640            4 :             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            4 :             status = LZ4F_compressEnd(state->ctx,
     652            4 :                                       state->buffer + state->bufdata,
     653            4 :                                       state->buflen - state->bufdata,
     654              :                                       NULL);
     655            4 :             if (LZ4F_isError(status))
     656              :             {
     657            0 :                 pg_log_error("could not end compression: %s",
     658              :                              LZ4F_getErrorName(status));
     659              :             }
     660              :             else
     661            4 :                 state->bufdata += status;
     662              : 
     663            4 :             errno = 0;
     664            4 :             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            4 :             status = LZ4F_freeCompressionContext(state->ctx);
     671            4 :             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            4 :         pg_free(state->buffer);
     685              :     }
     686              : 
     687            4 :     pg_free(state);
     688            4 :     CFH->private_data = NULL;
     689              : 
     690            4 :     errno = 0;
     691            4 :     ret = fclose(fp);
     692            4 :     if (ret != 0)
     693              :     {
     694            0 :         pg_log_error("could not close file: %m");
     695            0 :         return false;
     696              :     }
     697              : 
     698            4 :     return true;
     699              : }
     700              : 
     701              : static bool
     702            4 : LZ4Stream_open(const char *path, int fd, const char *mode,
     703              :                CompressFileHandle *CFH)
     704              : {
     705            4 :     LZ4State   *state = (LZ4State *) CFH->private_data;
     706              : 
     707            4 :     if (fd >= 0)
     708            0 :         state->fp = fdopen(dup(fd), mode);
     709              :     else
     710            4 :         state->fp = fopen(path, mode);
     711            4 :     if (state->fp == NULL)
     712              :     {
     713            0 :         state->errcode = errno;
     714            0 :         return false;
     715              :     }
     716              : 
     717            4 :     return true;
     718              : }
     719              : 
     720              : static bool
     721            3 : LZ4Stream_open_write(const char *path, const char *mode, CompressFileHandle *CFH)
     722              : {
     723              :     char       *fname;
     724              :     int         save_errno;
     725              :     bool        ret;
     726              : 
     727            3 :     fname = psprintf("%s.lz4", path);
     728            3 :     ret = CFH->open_func(fname, -1, mode, CFH);
     729              : 
     730            3 :     save_errno = errno;
     731            3 :     pg_free(fname);
     732            3 :     errno = save_errno;
     733              : 
     734            3 :     return ret;
     735              : }
     736              : 
     737              : /*
     738              :  * Public routines
     739              :  */
     740              : void
     741            4 : InitCompressFileHandleLZ4(CompressFileHandle *CFH,
     742              :                           const pg_compress_specification compression_spec)
     743              : {
     744              :     LZ4State   *state;
     745              : 
     746            4 :     CFH->open_func = LZ4Stream_open;
     747            4 :     CFH->open_write_func = LZ4Stream_open_write;
     748            4 :     CFH->read_func = LZ4Stream_read;
     749            4 :     CFH->write_func = LZ4Stream_write;
     750            4 :     CFH->gets_func = LZ4Stream_gets;
     751            4 :     CFH->getc_func = LZ4Stream_getc;
     752            4 :     CFH->eof_func = LZ4Stream_eof;
     753            4 :     CFH->close_func = LZ4Stream_close;
     754            4 :     CFH->get_error_func = LZ4Stream_get_error;
     755              : 
     756            4 :     CFH->compression_spec = compression_spec;
     757            4 :     state = pg_malloc0_object(LZ4State);
     758            4 :     if (CFH->compression_spec.level >= 0)
     759            4 :         state->prefs.compressionLevel = CFH->compression_spec.level;
     760              : 
     761            4 :     CFH->private_data = state;
     762            4 : }
     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.0-1