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.

◆ pglz_hist_add

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

Definition at line 296 of file pg_lzcompress.c.

◆ 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.

◆ 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.

◆ 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.

◆ 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.

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.

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}
int16_t int16
Definition: c.h:483
int32_t int32
Definition: c.h:484
static int16 hist_start[PGLZ_MAX_HISTORY_LISTS]
const PGLZ_Strategy *const PGLZ_strategy_default
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_MAX_MATCH
static PGLZ_HistEntry hist_entries[PGLZ_HISTORY_SIZE+1]
#define pglz_out_tag(_ctrlp, _ctrlb, _ctrl, _buf, _len, _off)
#define pglz_out_literal(_ctrlp, _ctrlb, _ctrl, _buf, _byte)
#define pglz_hist_add(_hs, _he, _hn, _recycle, _s, _e, _mask)
static rewind_source * source
Definition: pg_rewind.c:89
int32 first_success_by
Definition: pg_lzcompress.h:62
int32 match_size_drop
Definition: pg_lzcompress.h:64
int32 match_size_good
Definition: pg_lzcompress.h:63
int32 min_comp_rate
Definition: pg_lzcompress.h:61
int32 max_input_size
Definition: pg_lzcompress.h:60

References generate_unaccent_rules::dest, PGLZ_Strategy::first_success_by, hist_entries, 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, PGLZ_strategy_default, and source.

Referenced by pglz_compress_datum(), and XLogCompressBackupBlock().

◆ pglz_decompress()

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

Definition at line 692 of file pg_lzcompress.c.

694{
695 const unsigned char *sp;
696 const unsigned char *srcend;
697 unsigned char *dp;
698 unsigned char *destend;
699
700 sp = (const unsigned char *) source;
701 srcend = ((const unsigned char *) source) + slen;
702 dp = (unsigned char *) dest;
703 destend = dp + rawsize;
704
705 while (sp < srcend && dp < destend)
706 {
707 /*
708 * Read one control byte and process the next 8 items (or as many as
709 * remain in the compressed input).
710 */
711 unsigned char ctrl = *sp++;
712 int ctrlc;
713
714 for (ctrlc = 0; ctrlc < 8 && sp < srcend && dp < destend; ctrlc++)
715 {
716 if (ctrl & 1)
717 {
718 /*
719 * Set control bit means we must read a match tag. The match
720 * is coded with two bytes. First byte uses lower nibble to
721 * code length - 3. Higher nibble contains upper 4 bits of the
722 * offset. The next following byte contains the lower 8 bits
723 * of the offset. If the length is coded as 18, another
724 * extension tag byte tells how much longer the match really
725 * was (0-255).
726 */
727 int32 len;
728 int32 off;
729
730 len = (sp[0] & 0x0f) + 3;
731 off = ((sp[0] & 0xf0) << 4) | sp[1];
732 sp += 2;
733 if (len == 18)
734 len += *sp++;
735
736 /*
737 * Check for corrupt data: if we fell off the end of the
738 * source, or if we obtained off = 0, or if off is more than
739 * the distance back to the buffer start, we have problems.
740 * (We must check for off = 0, else we risk an infinite loop
741 * below in the face of corrupt data. Likewise, the upper
742 * limit on off prevents accessing outside the buffer
743 * boundaries.)
744 */
745 if (unlikely(sp > srcend || off == 0 ||
746 off > (dp - (unsigned char *) dest)))
747 return -1;
748
749 /*
750 * Don't emit more data than requested.
751 */
752 len = Min(len, destend - dp);
753
754 /*
755 * Now we copy the bytes specified by the tag from OUTPUT to
756 * OUTPUT (copy len bytes from dp - off to dp). The copied
757 * areas could overlap, so to avoid undefined behavior in
758 * memcpy(), be careful to copy only non-overlapping regions.
759 *
760 * Note that we cannot use memmove() instead, since while its
761 * behavior is well-defined, it's also not what we want.
762 */
763 while (off < len)
764 {
765 /*
766 * We can safely copy "off" bytes since that clearly
767 * results in non-overlapping source and destination.
768 */
769 memcpy(dp, dp - off, off);
770 len -= off;
771 dp += off;
772
773 /*----------
774 * This bit is less obvious: we can double "off" after
775 * each such step. Consider this raw input:
776 * 112341234123412341234
777 * This will be encoded as 5 literal bytes "11234" and
778 * then a match tag with length 16 and offset 4. After
779 * memcpy'ing the first 4 bytes, we will have emitted
780 * 112341234
781 * so we can double "off" to 8, then after the next step
782 * we have emitted
783 * 11234123412341234
784 * Then we can double "off" again, after which it is more
785 * than the remaining "len" so we fall out of this loop
786 * and finish with a non-overlapping copy of the
787 * remainder. In general, a match tag with off < len
788 * implies that the decoded data has a repeat length of
789 * "off". We can handle 1, 2, 4, etc repetitions of the
790 * repeated string per memcpy until we get to a situation
791 * where the final copy step is non-overlapping.
792 *
793 * (Another way to understand this is that we are keeping
794 * the copy source point dp - off the same throughout.)
795 *----------
796 */
797 off += off;
798 }
799 memcpy(dp, dp - off, len);
800 dp += len;
801 }
802 else
803 {
804 /*
805 * An unset control bit means LITERAL BYTE. So we just copy
806 * one from INPUT to OUTPUT.
807 */
808 *dp++ = *sp++;
809 }
810
811 /*
812 * Advance the control bit
813 */
814 ctrl >>= 1;
815 }
816 }
817
818 /*
819 * If requested, check we decompressed the right amount.
820 */
821 if (check_complete && (dp != destend || sp != srcend))
822 return -1;
823
824 /*
825 * That's it.
826 */
827 return (char *) dp - dest;
828}
#define Min(x, y)
Definition: c.h:961
#define unlikely(x)
Definition: c.h:333
const void size_t len

References generate_unaccent_rules::dest, len, Min, source, and unlikely.

Referenced by pglz_decompress_datum(), pglz_decompress_datum_slice(), and RestoreBlockImage().

◆ 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.

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}
FILE * input
#define INVALID_ENTRY_PTR
#define pglz_hist_idx(_s, _e, _mask)
const char * pos
struct PGLZ_HistEntry * next

References hist_entries, input, INVALID_ENTRY_PTR, len, PGLZ_HistEntry::next, pglz_hist_idx, PGLZ_MAX_MATCH, and PGLZ_HistEntry::pos.

Referenced by pglz_compress().

◆ pglz_maximum_compressed_size()

int32 pglz_maximum_compressed_size ( int32  rawsize,
int32  total_compressed_size 
)

Definition at line 846 of file pg_lzcompress.c.

847{
848 int64 compressed_size;
849
850 /*
851 * pglz uses one control bit per byte, so if the entire desired prefix is
852 * represented as literal bytes, we'll need (rawsize * 9) bits. We care
853 * about bytes though, so be sure to round up not down.
854 *
855 * Use int64 here to prevent overflow during calculation.
856 */
857 compressed_size = ((int64) rawsize * 9 + 7) / 8;
858
859 /*
860 * The above fails to account for a corner case: we could have compressed
861 * data that starts with N-1 or N-2 literal bytes and then has a match tag
862 * of 2 or 3 bytes. It's therefore possible that we need to fetch 1 or 2
863 * more bytes in order to have the whole match tag. (Match tags earlier
864 * in the compressed data don't cause a problem, since they should
865 * represent more decompressed bytes than they occupy themselves.)
866 */
867 compressed_size += 2;
868
869 /*
870 * Maximum compressed size can't be larger than total compressed size.
871 * (This also ensures that our result fits in int32.)
872 */
873 compressed_size = Min(compressed_size, total_compressed_size);
874
875 return (int32) compressed_size;
876}
int64_t int64
Definition: c.h:485

References Min.

Referenced by detoast_attr_slice().

Variable Documentation

◆ hist_entries

PGLZ_HistEntry hist_entries[PGLZ_HISTORY_SIZE+1]
static

Definition at line 256 of file pg_lzcompress.c.

Referenced by pglz_compress(), and pglz_find_match().

◆ 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(), pglz_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.