LCOV - code coverage report
Current view: top level - src/bin/pg_dump - compress_lz4.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 161 282 57.1 %
Date: 2025-10-31 10:19:07 Functions: 11 17 64.7 %
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-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 */

Generated by: LCOV version 1.16