File Coverage

blib/lib/MySQL/Compress.pm
Criterion Covered Total %
statement 61 70 87.1
branch 25 36 69.4
condition 11 18 61.1
subroutine 8 8 100.0
pod 3 3 100.0
total 108 135 80.0


line stmt bran cond sub pod time code
1             package MySQL::Compress;
2             # $Id: Compress.pm,v 1.2 2019/02/23 03:38:24 cmanley Exp $
3 2     2   136152 use strict;
  2         12  
  2         57  
4 2     2   11 use warnings;
  2         4  
  2         47  
5 2     2   1153 use Compress::Zlib ();
  2         125254  
  2         63  
6 2     2   16 use Carp qw(croak);
  2         6  
  2         101  
7 2     2   12 use base qw(Exporter);
  2         5  
  2         1592  
8             our @EXPORT = ();
9             our @EXPORT_OK = qw(
10             mysql_compress
11             mysql_uncompress
12             mysql_uncompressed_length
13             );
14             our %EXPORT_TAGS = (
15             'all' => \@EXPORT_OK,
16             );
17             our $VERSION = sprintf '%d.%02d', q{$Revision: 1.2 $} =~ m/ (\d+) \. (\d+) /xg;
18              
19             =head1 NAME
20              
21             MySQL::Compress - MySQL COMPRESS() and UNCOMPRESS() compatible Perl functions
22              
23             =head1 DESCRIPTION
24              
25             This module provides functions compatible with MySQL COMPRESS() and UNCOMPRESS().
26             One reason you may want to use these functions is because MySQL COMPRESS() does not offer the possibilty
27             to specify the compression level, whereas the mysql_compress() function in this module does.
28              
29             =head1 SYNOPSIS
30              
31             # Import all functions:
32             use MySQL::Compress qw(:all)
33              
34             # Or list the functions you want to import:
35             use MySQL::Compress qw(
36             mysql_compress
37             mysql_uncompress
38             mysql_uncompressed_length
39             );
40              
41             # Emulate COMPRESS():
42             my $compressed_string = mysql_compress('Hello world!');
43              
44             # Emulate UNCOMPRESS():
45             print mysql_uncompress($compressed_string) . "\n"; # prints "Hello world!\n"
46              
47             # Emulate UNCOMPRESSED_LENGTH():
48             my $len = mysql_uncompressed_length($compressed_string); # $len equals byte length of "Hello world!"
49              
50             =head1 EXPORTS
51              
52             The following functions can be imported into the calling namespace by request:
53              
54             mysql_compress
55             mysql_uncompress
56             mysql_uncompressed_length
57             :all - what it says
58              
59             =head1 FUNCTIONS
60              
61             =over
62              
63             =item $dest = mysql_compress($source [, $level])
64              
65             MySQL COMPRESS() compatible compression function.
66             $level is the optional compression level (valid values are 0 through 9; default = 6). See L documentation for details.
67             Returns the compressed data on success, else undef.
68              
69             From the MySQL COMPRESS() documentation:
70             The compressed string contents are stored the following way:
71             - Empty strings are stored as empty strings.
72             - Nonempty strings are stored as a 4-byte length of the uncompressed string (low byte first), followed by the compressed string.
73             If the string ends with space, an extra "." character is added to avoid problems with endspace trimming should the result be
74             stored in a CHAR or VARCHAR column. (However, use of nonbinary string data types such as CHAR or VARCHAR to store compressed
75             strings is not recommended anyway because character set conversion may occur. Use a VARBINARY or BLOB binary string column instead.)
76              
77             =cut
78              
79             sub mysql_compress {
80 17 50 33 17 1 7927 my $proto = @_ && UNIVERSAL::isa($_[0],__PACKAGE__) ? shift : __PACKAGE__;
81 17         36 my $source = shift;
82 17         19 my $level = shift;
83 17 100 100     59 unless (defined($source) && length($source)) {
84 4         13 return $source;
85             }
86 13 50 66     68 unless (defined($level) && (length($level) == 1) && ($level =~ /^\d$/)) {
      66        
87             #$level = 6;
88 6         16 $level = Compress::Zlib::Z_DEFAULT_COMPRESSION; # -1
89             }
90 13         73 require bytes;
91 13         36 my $result = pack('V', bytes::length($source)) . Compress::Zlib::compress($source, $level);
92 13 100       3720 if (substr($result,-1) eq ' ') {
93 2         12 $result .= '.';
94             }
95 13         48 return $result;
96             }
97              
98              
99              
100              
101             =item $dest = mysql_uncompress($source)
102              
103             MySQL UNCOMPRESS() compatible function.
104             Uncompresses data that has been compressed with MySQL's COMPRESS() function.
105             $source can be either a scalar or a scalar reference.
106             Returns the uncompressed data on success, else undef.
107              
108             =cut
109              
110             sub mysql_uncompress {
111 21 50 33 21 1 3645 my $proto = @_ && UNIVERSAL::isa($_[0],__PACKAGE__) ? shift : __PACKAGE__;
112 21         49 my $source = shift;
113 21 100       63 unless (defined($source)) {
114 3         7 return $source;
115             }
116 18         27 my $ref;
117 18 50       33 if (ref($source)) {
118 0 0       0 unless (defined($$source)) {
119 0         0 return $$source;
120             }
121 0         0 $ref = $source;
122             }
123             else {
124 18         29 $ref = \$source;
125             }
126 18         40 my $expect_len = $proto->mysql_uncompressed_length($ref);
127 18 50       40 if (!defined($expect_len)) {
128 0         0 return $expect_len;
129             }
130 18 100       36 if ($expect_len == 0) {
131 3         7 return '';
132             }
133 15         47 my $result = Compress::Zlib::uncompress(substr($$ref, 4));
134 15 50       631 if (defined($result)) {
135 15         54 require bytes;
136 15         36 my $actual_len = bytes::length($result);
137 15 50       52 if ($expect_len != $actual_len) {
138 0         0 warn "mysql_uncompress: Unexpected uncompressed data length (expected=$expect_len, got=$actual_len)";
139 0         0 return undef;
140             }
141             }
142 15         54 return $result;
143             }
144              
145              
146              
147              
148             =item $length = mysql_uncompressed_length($source)
149              
150             Returns the expected uncompressed length of the given string that has been compressed with MySQL's COMPRESS() function.
151             This is done without actually decompressing since COMPRESS() prepends the length to the compressed string.
152             $source can be either a scalar or a scalar reference.
153             Returns the expected uncompressed length on success, else undef.
154              
155             =cut
156              
157             sub mysql_uncompressed_length {
158 25 100 66 25 1 4980 my $proto = @_ && UNIVERSAL::isa($_[0],__PACKAGE__) ? shift : __PACKAGE__;
159 25         42 my $source = shift;
160 25 100       52 unless (defined($source)) {
161 1         4 return $source;
162             }
163 24         31 my $ref;
164 24 100       45 if (ref($source)) {
165 18 50       38 unless (defined($$source)) {
166 0         0 return $$source;
167             }
168 18         39 $ref = $source;
169             }
170             else {
171 6         10 $ref = \$source;
172             }
173 24         36 my $min_compressed_length = 13;
174 24         114 require bytes;
175 24         54 my $len = bytes::length($$ref);
176 24 100       90 if ($len < $min_compressed_length) {
177 4 50       10 if ($len == 0) { # COMPRESS() returns an empty string when given an empty string.
178 4         10 return 0;
179             }
180 0         0 warn "mysql_uncompressed_length: Given compressed string has a length of $len which is less than the minimum possible compressed length of $min_compressed_length";
181 0         0 return undef;
182             }
183 20         66 my $expect_len = unpack('V', substr($$ref,0,4));
184 20         42 return $expect_len;
185             }
186              
187              
188              
189             1;
190              
191             __END__