PostgreSQL Source Code  git master
pg_lzcompress.c File Reference
#include "postgres.h"
#include <limits.h>
#include "common/pg_lzcompress.h"
Include dependency graph for pg_lzcompress.c:

Go to the source code of this file.

Data Structures

struct  PGLZ_HistEntry
 

Macros

#define PGLZ_MAX_HISTORY_LISTS   8192 /* must be power of 2 */
 
#define PGLZ_HISTORY_SIZE   4096
 
#define PGLZ_MAX_MATCH   273
 
#define INVALID_ENTRY   0
 
#define INVALID_ENTRY_PTR   (&hist_entries[INVALID_ENTRY])
 
#define pglz_hist_idx(_s, _e, _mask)
 
#define pglz_hist_add(_hs, _he, _hn, _recycle, _s, _e, _mask)
 
#define pglz_out_ctrl(__ctrlp, __ctrlb, __ctrl, __buf)
 
#define pglz_out_literal(_ctrlp, _ctrlb, _ctrl, _buf, _byte)
 
#define pglz_out_tag(_ctrlp, _ctrlb, _ctrl, _buf, _len, _off)
 

Typedefs

typedef struct PGLZ_HistEntry PGLZ_HistEntry
 

Functions

static int pglz_find_match (int16 *hstart, const char *input, const char *end, int *lenp, int *offp, int good_match, int good_drop, int mask)
 
int32 pglz_compress (const char *source, int32 slen, char *dest, const PGLZ_Strategy *strategy)
 
int32 pglz_decompress (const char *source, int32 slen, char *dest, int32 rawsize, bool check_complete)
 
int32 pglz_maximum_compressed_size (int32 rawsize, int32 total_compressed_size)
 

Variables

static const PGLZ_Strategy strategy_default_data
 
const PGLZ_Strategy *const PGLZ_strategy_default = &strategy_default_data
 
static const PGLZ_Strategy strategy_always_data
 
const PGLZ_Strategy *const PGLZ_strategy_always = &strategy_always_data
 
static int16 hist_start [PGLZ_MAX_HISTORY_LISTS]
 
static PGLZ_HistEntry hist_entries [PGLZ_HISTORY_SIZE+1]
 

Macro Definition Documentation

◆ INVALID_ENTRY

#define INVALID_ENTRY   0

Definition at line 262 of file pg_lzcompress.c.

◆ INVALID_ENTRY_PTR

#define INVALID_ENTRY_PTR   (&hist_entries[INVALID_ENTRY])

Definition at line 263 of file pg_lzcompress.c.

Referenced by pglz_find_match().

◆ pglz_hist_add

#define pglz_hist_add (   _hs,
  _he,
  _hn,
  _recycle,
  _s,
  _e,
  _mask 
)

Definition at line 296 of file pg_lzcompress.c.

Referenced by pglz_compress().

◆ pglz_hist_idx

#define pglz_hist_idx (   _s,
  _e,
  _mask 
)
Value:
( \
((((_e) - (_s)) < 4) ? (int) (_s)[0] : \
(((_s)[0] << 6) ^ ((_s)[1] << 4) ^ \
((_s)[2] << 2) ^ (_s)[3])) & (_mask) \
)

Definition at line 277 of file pg_lzcompress.c.

Referenced by pglz_find_match().

◆ PGLZ_HISTORY_SIZE

#define PGLZ_HISTORY_SIZE   4096

Definition at line 196 of file pg_lzcompress.c.

◆ PGLZ_MAX_HISTORY_LISTS

#define PGLZ_MAX_HISTORY_LISTS   8192 /* must be power of 2 */

Definition at line 195 of file pg_lzcompress.c.

◆ PGLZ_MAX_MATCH

#define PGLZ_MAX_MATCH   273

Definition at line 197 of file pg_lzcompress.c.

Referenced by pglz_compress(), and pglz_find_match().

◆ pglz_out_ctrl

#define pglz_out_ctrl (   __ctrlp,
  __ctrlb,
  __ctrl,
  __buf 
)
Value:
do { \
if ((__ctrl & 0xff) == 0) \
{ \
*(__ctrlp) = __ctrlb; \
__ctrlp = (__buf)++; \
__ctrlb = 0; \
__ctrl = 1; \
} \
} while (0)

Definition at line 336 of file pg_lzcompress.c.

◆ pglz_out_literal

#define pglz_out_literal (   _ctrlp,
  _ctrlb,
  _ctrl,
  _buf,
  _byte 
)
Value:
do { \
pglz_out_ctrl(_ctrlp,_ctrlb,_ctrl,_buf); \
*(_buf)++ = (unsigned char)(_byte); \
_ctrl <<= 1; \
} while (0)

Definition at line 355 of file pg_lzcompress.c.

Referenced by pglz_compress().

◆ pglz_out_tag

#define pglz_out_tag (   _ctrlp,
  _ctrlb,
  _ctrl,
  _buf,
  _len,
  _off 
)
Value:
do { \
pglz_out_ctrl(_ctrlp,_ctrlb,_ctrl,_buf); \
_ctrlb |= _ctrl; \
_ctrl <<= 1; \
if (_len > 17) \
{ \
(_buf)[0] = (unsigned char)((((_off) & 0xf00) >> 4) | 0x0f); \
(_buf)[1] = (unsigned char)(((_off) & 0xff)); \
(_buf)[2] = (unsigned char)((_len) - 18); \
(_buf) += 3; \
} else { \
(_buf)[0] = (unsigned char)((((_off) & 0xf00) >> 4) | ((_len) - 3)); \
(_buf)[1] = (unsigned char)((_off) & 0xff); \
(_buf) += 2; \
} \
} while (0)

Definition at line 371 of file pg_lzcompress.c.

Referenced by pglz_compress().

Typedef Documentation

◆ PGLZ_HistEntry

Function Documentation

◆ pglz_compress()

int32 pglz_compress ( const char *  source,
int32  slen,
char *  dest,
const PGLZ_Strategy strategy 
)

Definition at line 509 of file pg_lzcompress.c.

References PGLZ_Strategy::first_success_by, hist_start, PGLZ_Strategy::match_size_drop, PGLZ_Strategy::match_size_good, PGLZ_Strategy::max_input_size, PGLZ_Strategy::min_comp_rate, pglz_find_match(), pglz_hist_add, PGLZ_MAX_MATCH, pglz_out_literal, pglz_out_tag, and PGLZ_strategy_default.

Referenced by toast_compress_datum(), and XLogCompressBackupBlock().

511 {
512  unsigned char *bp = (unsigned char *) dest;
513  unsigned char *bstart = bp;
514  int hist_next = 1;
515  bool hist_recycle = false;
516  const char *dp = source;
517  const char *dend = source + slen;
518  unsigned char ctrl_dummy = 0;
519  unsigned char *ctrlp = &ctrl_dummy;
520  unsigned char ctrlb = 0;
521  unsigned char ctrl = 0;
522  bool found_match = false;
523  int32 match_len;
524  int32 match_off;
525  int32 good_match;
526  int32 good_drop;
527  int32 result_size;
528  int32 result_max;
529  int32 need_rate;
530  int hashsz;
531  int mask;
532 
533  /*
534  * Our fallback strategy is the default.
535  */
536  if (strategy == NULL)
537  strategy = PGLZ_strategy_default;
538 
539  /*
540  * If the strategy forbids compression (at all or if source chunk size out
541  * of range), fail.
542  */
543  if (strategy->match_size_good <= 0 ||
544  slen < strategy->min_input_size ||
545  slen > strategy->max_input_size)
546  return -1;
547 
548  /*
549  * Limit the match parameters to the supported range.
550  */
551  good_match = strategy->match_size_good;
552  if (good_match > PGLZ_MAX_MATCH)
553  good_match = PGLZ_MAX_MATCH;
554  else if (good_match < 17)
555  good_match = 17;
556 
557  good_drop = strategy->match_size_drop;
558  if (good_drop < 0)
559  good_drop = 0;
560  else if (good_drop > 100)
561  good_drop = 100;
562 
563  need_rate = strategy->min_comp_rate;
564  if (need_rate < 0)
565  need_rate = 0;
566  else if (need_rate > 99)
567  need_rate = 99;
568 
569  /*
570  * Compute the maximum result size allowed by the strategy, namely the
571  * input size minus the minimum wanted compression rate. This had better
572  * be <= slen, else we might overrun the provided output buffer.
573  */
574  if (slen > (INT_MAX / 100))
575  {
576  /* Approximate to avoid overflow */
577  result_max = (slen / 100) * (100 - need_rate);
578  }
579  else
580  result_max = (slen * (100 - need_rate)) / 100;
581 
582  /*
583  * Experiments suggest that these hash sizes work pretty well. A large
584  * hash table minimizes collision, but has a higher startup cost. For a
585  * small input, the startup cost dominates. The table size must be a power
586  * of two.
587  */
588  if (slen < 128)
589  hashsz = 512;
590  else if (slen < 256)
591  hashsz = 1024;
592  else if (slen < 512)
593  hashsz = 2048;
594  else if (slen < 1024)
595  hashsz = 4096;
596  else
597  hashsz = 8192;
598  mask = hashsz - 1;
599 
600  /*
601  * Initialize the history lists to empty. We do not need to zero the
602  * hist_entries[] array; its entries are initialized as they are used.
603  */
604  memset(hist_start, 0, hashsz * sizeof(int16));
605 
606  /*
607  * Compress the source directly into the output buffer.
608  */
609  while (dp < dend)
610  {
611  /*
612  * If we already exceeded the maximum result size, fail.
613  *
614  * We check once per loop; since the loop body could emit as many as 4
615  * bytes (a control byte and 3-byte tag), PGLZ_MAX_OUTPUT() had better
616  * allow 4 slop bytes.
617  */
618  if (bp - bstart >= result_max)
619  return -1;
620 
621  /*
622  * If we've emitted more than first_success_by bytes without finding
623  * anything compressible at all, fail. This lets us fall out
624  * reasonably quickly when looking at incompressible input (such as
625  * pre-compressed data).
626  */
627  if (!found_match && bp - bstart >= strategy->first_success_by)
628  return -1;
629 
630  /*
631  * Try to find a match in the history
632  */
633  if (pglz_find_match(hist_start, dp, dend, &match_len,
634  &match_off, good_match, good_drop, mask))
635  {
636  /*
637  * Create the tag and add history entries for all matched
638  * characters.
639  */
640  pglz_out_tag(ctrlp, ctrlb, ctrl, bp, match_len, match_off);
641  while (match_len--)
642  {
644  hist_next, hist_recycle,
645  dp, dend, mask);
646  dp++; /* Do not do this ++ in the line above! */
647  /* The macro would do it four times - Jan. */
648  }
649  found_match = true;
650  }
651  else
652  {
653  /*
654  * No match found. Copy one literal byte.
655  */
656  pglz_out_literal(ctrlp, ctrlb, ctrl, bp, *dp);
658  hist_next, hist_recycle,
659  dp, dend, mask);
660  dp++; /* Do not do this ++ in the line above! */
661  /* The macro would do it four times - Jan. */
662  }
663  }
664 
665  /*
666  * Write out the last control byte and check that we haven't overrun the
667  * output size allowed by the strategy.
668  */
669  *ctrlp = ctrlb;
670  result_size = bp - bstart;
671  if (result_size >= result_max)
672  return -1;
673 
674  /* success */
675  return result_size;
676 }
signed short int16
Definition: c.h:346
#define pglz_hist_add(_hs, _he, _hn, _recycle, _s, _e, _mask)
static PGLZ_HistEntry hist_entries[PGLZ_HISTORY_SIZE+1]
signed int int32
Definition: c.h:347
int32 first_success_by
Definition: pg_lzcompress.h:62
int32 match_size_good
Definition: pg_lzcompress.h:63
#define pglz_out_tag(_ctrlp, _ctrlb, _ctrl, _buf, _len, _off)
int32 match_size_drop
Definition: pg_lzcompress.h:64
static int16 hist_start[PGLZ_MAX_HISTORY_LISTS]
static int pglz_find_match(int16 *hstart, const char *input, const char *end, int *lenp, int *offp, int good_match, int good_drop, int mask)
#define pglz_out_literal(_ctrlp, _ctrlb, _ctrl, _buf, _byte)
#define PGLZ_MAX_MATCH
int32 max_input_size
Definition: pg_lzcompress.h:60
const PGLZ_Strategy *const PGLZ_strategy_default
int32 min_comp_rate
Definition: pg_lzcompress.h:61

◆ pglz_decompress()

int32 pglz_decompress ( const char *  source,
int32  slen,
char *  dest,
int32  rawsize,
bool  check_complete 
)

Definition at line 689 of file pg_lzcompress.c.

References generate_unaccent_rules::dest, and Min.

Referenced by RestoreBlockImage(), toast_decompress_datum(), and toast_decompress_datum_slice().

691 {
692  const unsigned char *sp;
693  const unsigned char *srcend;
694  unsigned char *dp;
695  unsigned char *destend;
696 
697  sp = (const unsigned char *) source;
698  srcend = ((const unsigned char *) source) + slen;
699  dp = (unsigned char *) dest;
700  destend = dp + rawsize;
701 
702  while (sp < srcend && dp < destend)
703  {
704  /*
705  * Read one control byte and process the next 8 items (or as many as
706  * remain in the compressed input).
707  */
708  unsigned char ctrl = *sp++;
709  int ctrlc;
710 
711  for (ctrlc = 0; ctrlc < 8 && sp < srcend && dp < destend; ctrlc++)
712  {
713 
714  if (ctrl & 1)
715  {
716  /*
717  * Set control bit means we must read a match tag. The match
718  * is coded with two bytes. First byte uses lower nibble to
719  * code length - 3. Higher nibble contains upper 4 bits of the
720  * offset. The next following byte contains the lower 8 bits
721  * of the offset. If the length is coded as 18, another
722  * extension tag byte tells how much longer the match really
723  * was (0-255).
724  */
725  int32 len;
726  int32 off;
727 
728  len = (sp[0] & 0x0f) + 3;
729  off = ((sp[0] & 0xf0) << 4) | sp[1];
730  sp += 2;
731  if (len == 18)
732  len += *sp++;
733 
734  /*
735  * Now we copy the bytes specified by the tag from OUTPUT to
736  * OUTPUT (copy len bytes from dp - off to dp). The copied
737  * areas could overlap, to preven possible uncertainty, we
738  * copy only non-overlapping regions.
739  */
740  len = Min(len, destend - dp);
741  while (off < len)
742  {
743  /*---------
744  * When offset is smaller than length - source and
745  * destination regions overlap. memmove() is resolving
746  * this overlap in an incompatible way with pglz. Thus we
747  * resort to memcpy()-ing non-overlapping regions.
748  *
749  * Consider input: 112341234123412341234
750  * At byte 5 here ^ we have match with length 16 and
751  * offset 4. 11234M(len=16, off=4)
752  * We are decoding first period of match and rewrite match
753  * 112341234M(len=12, off=8)
754  *
755  * The same match is now at position 9, it points to the
756  * same start byte of output, but from another position:
757  * the offset is doubled.
758  *
759  * We iterate through this offset growth until we can
760  * proceed to usual memcpy(). If we would try to decode
761  * the match at byte 5 (len=16, off=4) by memmove() we
762  * would issue memmove(5, 1, 16) which would produce
763  * 112341234XXXXXXXXXXXX, where series of X is 12
764  * undefined bytes, that were at bytes [5:17].
765  *---------
766  */
767  memcpy(dp, dp - off, off);
768  len -= off;
769  dp += off;
770  off += off;
771  }
772  memcpy(dp, dp - off, len);
773  dp += len;
774  }
775  else
776  {
777  /*
778  * An unset control bit means LITERAL BYTE. So we just copy
779  * one from INPUT to OUTPUT.
780  */
781  *dp++ = *sp++;
782  }
783 
784  /*
785  * Advance the control bit
786  */
787  ctrl >>= 1;
788  }
789  }
790 
791  /*
792  * Check we decompressed the right amount. If we are slicing, then we
793  * won't necessarily be at the end of the source or dest buffers when we
794  * hit a stop, so we don't test them.
795  */
796  if (check_complete && (dp != destend || sp != srcend))
797  return -1;
798 
799  /*
800  * That's it.
801  */
802  return (char *) dp - dest;
803 }
#define Min(x, y)
Definition: c.h:911
signed int int32
Definition: c.h:347

◆ pglz_find_match()

static int pglz_find_match ( int16 hstart,
const char *  input,
const char *  end,
int *  lenp,
int *  offp,
int  good_match,
int  good_drop,
int  mask 
)
inlinestatic

Definition at line 399 of file pg_lzcompress.c.

References INVALID_ENTRY_PTR, PGLZ_HistEntry::next, pglz_hist_idx, PGLZ_MAX_MATCH, and PGLZ_HistEntry::pos.

Referenced by pglz_compress().

401 {
402  PGLZ_HistEntry *hent;
403  int16 hentno;
404  int32 len = 0;
405  int32 off = 0;
406 
407  /*
408  * Traverse the linked history list until a good enough match is found.
409  */
410  hentno = hstart[pglz_hist_idx(input, end, mask)];
411  hent = &hist_entries[hentno];
412  while (hent != INVALID_ENTRY_PTR)
413  {
414  const char *ip = input;
415  const char *hp = hent->pos;
416  int32 thisoff;
417  int32 thislen;
418 
419  /*
420  * Stop if the offset does not fit into our tag anymore.
421  */
422  thisoff = ip - hp;
423  if (thisoff >= 0x0fff)
424  break;
425 
426  /*
427  * Determine length of match. A better match must be larger than the
428  * best so far. And if we already have a match of 16 or more bytes,
429  * it's worth the call overhead to use memcmp() to check if this match
430  * is equal for the same size. After that we must fallback to
431  * character by character comparison to know the exact position where
432  * the diff occurred.
433  */
434  thislen = 0;
435  if (len >= 16)
436  {
437  if (memcmp(ip, hp, len) == 0)
438  {
439  thislen = len;
440  ip += len;
441  hp += len;
442  while (ip < end && *ip == *hp && thislen < PGLZ_MAX_MATCH)
443  {
444  thislen++;
445  ip++;
446  hp++;
447  }
448  }
449  }
450  else
451  {
452  while (ip < end && *ip == *hp && thislen < PGLZ_MAX_MATCH)
453  {
454  thislen++;
455  ip++;
456  hp++;
457  }
458  }
459 
460  /*
461  * Remember this match as the best (if it is)
462  */
463  if (thislen > len)
464  {
465  len = thislen;
466  off = thisoff;
467  }
468 
469  /*
470  * Advance to the next history entry
471  */
472  hent = hent->next;
473 
474  /*
475  * Be happy with lesser good matches the more entries we visited. But
476  * no point in doing calculation if we're at end of list.
477  */
478  if (hent != INVALID_ENTRY_PTR)
479  {
480  if (len >= good_match)
481  break;
482  good_match -= (good_match * good_drop) / 100;
483  }
484  }
485 
486  /*
487  * Return match information only if it results at least in one byte
488  * reduction.
489  */
490  if (len > 2)
491  {
492  *lenp = len;
493  *offp = off;
494  return 1;
495  }
496 
497  return 0;
498 }
signed short int16
Definition: c.h:346
struct PGLZ_HistEntry * next
const char * pos
static PGLZ_HistEntry hist_entries[PGLZ_HISTORY_SIZE+1]
signed int int32
Definition: c.h:347
#define PGLZ_MAX_MATCH
#define INVALID_ENTRY_PTR
#define pglz_hist_idx(_s, _e, _mask)

◆ pglz_maximum_compressed_size()

int32 pglz_maximum_compressed_size ( int32  rawsize,
int32  total_compressed_size 
)

Definition at line 821 of file pg_lzcompress.c.

References Min.

Referenced by detoast_attr_slice().

822 {
823  int32 compressed_size;
824 
825  /*
826  * pglz uses one control bit per byte, so we need (rawsize * 9) bits. We
827  * care about bytes though, so we add 7 to make sure we include the last
828  * incomplete byte (integer division rounds down).
829  *
830  * XXX Use int64 to prevent overflow during calculation.
831  */
832  compressed_size = (int32) ((int64) rawsize * 9 + 7) / 8;
833 
834  /*
835  * Maximum compressed size can't be larger than total compressed size.
836  */
837  compressed_size = Min(compressed_size, total_compressed_size);
838 
839  return compressed_size;
840 }
#define Min(x, y)
Definition: c.h:911
signed int int32
Definition: c.h:347

Variable Documentation

◆ hist_entries

PGLZ_HistEntry hist_entries[PGLZ_HISTORY_SIZE+1]
static

Definition at line 256 of file pg_lzcompress.c.

◆ hist_start

int16 hist_start[PGLZ_MAX_HISTORY_LISTS]
static

Definition at line 255 of file pg_lzcompress.c.

Referenced by pglz_compress().

◆ PGLZ_strategy_always

const PGLZ_Strategy* const PGLZ_strategy_always = &strategy_always_data

Definition at line 248 of file pg_lzcompress.c.

◆ PGLZ_strategy_default

const PGLZ_Strategy* const PGLZ_strategy_default = &strategy_default_data

Definition at line 236 of file pg_lzcompress.c.

Referenced by pglz_compress(), toast_compress_datum(), and XLogCompressBackupBlock().

◆ strategy_always_data

const PGLZ_Strategy strategy_always_data
static
Initial value:
= {
0,
INT_MAX,
0,
INT_MAX,
128,
6
}

Definition at line 239 of file pg_lzcompress.c.

◆ strategy_default_data

const PGLZ_Strategy strategy_default_data
static
Initial value:
= {
32,
INT_MAX,
25,
1024,
128,
10
}

Definition at line 223 of file pg_lzcompress.c.