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.1 2019/02/23 02:55:55 cmanley Exp $
3 2     2   109807 use strict;
  2         11  
  2         47  
4 2     2   7 use warnings;
  2         4  
  2         38  
5 2     2   912 use Compress::Zlib ();
  2         101044  
  2         50  
6 2     2   28 use Carp qw(croak);
  2         3  
  2         85  
7 2     2   10 use base qw(Exporter);
  2         4  
  2         1315  
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.1 $} =~ 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             use MySQL::Compress qw(
32             mysql_compress
33             mysql_uncompress
34             );
35              
36             Functional interface use:
37              
38             use MySQL::Compress qw(mysql_compress);
39             my $password = 'secret';
40             my $crypted_string = mysql_compress($password);
41              
42             Functional interface use, using options:
43              
44             use MySQL::Compress qw(:all);
45             my $password = 'secret';
46             my $crypted_string = mysql_compress($password, PASSWORD_DEFAULT, cost => 11);
47              
48             Class method use, using options:
49              
50             use MySQL::Compress;
51             my $password = 'secret';
52             my $crypted_string = MySQL::Compress->hash($password, cost => 9);
53             # Note that the 2nd argument of mysql_compress() has been dropped here and may be specified
54             # as an option as should've been the case in the original mysql_compress() function IMHO.
55              
56             =head1 EXPORTS
57              
58             The following functions can be imported into the calling namespace by request:
59              
60             mysql_compress
61             mysql_uncompress
62             mysql_uncompressed_length
63             :all - what it says
64              
65             =head1 FUNCTIONS
66              
67             =over
68              
69             =item $dest = mysql_compress($source [, $level])
70              
71             MySQL COMPRESS() compatible compression function.
72             $level is the optional compression level (valid values are 0 through 9; default = 6). See L documentation for details.
73             Returns the compressed data on success, else undef.
74              
75             From the MySQL COMPRESS() documentation:
76             The compressed string contents are stored the following way:
77             - Empty strings are stored as empty strings.
78             - Nonempty strings are stored as a 4-byte length of the uncompressed string (low byte first), followed by the compressed string.
79             If the string ends with space, an extra "." character is added to avoid problems with endspace trimming should the result be
80             stored in a CHAR or VARCHAR column. (However, use of nonbinary string data types such as CHAR or VARCHAR to store compressed
81             strings is not recommended anyway because character set conversion may occur. Use a VARBINARY or BLOB binary string column instead.)
82              
83             =cut
84              
85             sub mysql_compress {
86 17 50 33 17 1 6458 my $proto = @_ && UNIVERSAL::isa($_[0],__PACKAGE__) ? shift : __PACKAGE__;
87 17         27 my $source = shift;
88 17         18 my $level = shift;
89 17 100 100     49 unless (defined($source) && length($source)) {
90 4         9 return $source;
91             }
92 13 50 66     52 unless (defined($level) && (length($level) == 1) && ($level =~ /^\d$/)) {
      66        
93             #$level = 6;
94 6         14 $level = Compress::Zlib::Z_DEFAULT_COMPRESSION; # -1
95             }
96 13         60 require bytes;
97 13         28 my $result = pack('V', bytes::length($source)) . Compress::Zlib::compress($source, $level);
98 13 100       3055 if (substr($result,-1) eq ' ') {
99 2         5 $result .= '.';
100             }
101 13         38 return $result;
102             }
103              
104              
105              
106              
107             =item $dest = mysql_uncompress($source)
108              
109             MySQL UNCOMPRESS() compatible function.
110             Uncompresses data that has been compressed with MySQL's COMPRESS() function.
111             $source can be either a scalar or a scalar reference.
112             Returns the uncompressed data on success, else undef.
113              
114             =cut
115              
116             sub mysql_uncompress {
117 21 50 33 21 1 2985 my $proto = @_ && UNIVERSAL::isa($_[0],__PACKAGE__) ? shift : __PACKAGE__;
118 21         38 my $source = shift;
119 21 100       45 unless (defined($source)) {
120 3         5 return $source;
121             }
122 18         18 my $ref;
123 18 50       26 if (ref($source)) {
124 0 0       0 unless (defined($$source)) {
125 0         0 return $$source;
126             }
127 0         0 $ref = $source;
128             }
129             else {
130 18         24 $ref = \$source;
131             }
132 18         34 my $expect_len = $proto->mysql_uncompressed_length($ref);
133 18 50       45 if (!defined($expect_len)) {
134 0         0 return $expect_len;
135             }
136 18 100       24 if ($expect_len == 0) {
137 3         9 return '';
138             }
139 15         46 my $result = Compress::Zlib::uncompress(substr($$ref, 4));
140 15 50       576 if (defined($result)) {
141 15         45 require bytes;
142 15         29 my $actual_len = bytes::length($result);
143 15 50       57 if ($expect_len != $actual_len) {
144 0         0 warn "mysql_uncompress: Unexpected uncompressed data length (expected=$expect_len, got=$actual_len)";
145 0         0 return undef;
146             }
147             }
148 15         34 return $result;
149             }
150              
151              
152              
153              
154             =item $length = mysql_uncompressed_length($source)
155              
156             Returns the expected uncompressed length of the given string that has been compressed with MySQL's COMPRESS() function.
157             This is done without actually decompressing since COMPRESS() prepends the length to the compressed string.
158             $source can be either a scalar or a scalar reference.
159             Returns the expected uncompressed length on success, else undef.
160              
161             =cut
162              
163             sub mysql_uncompressed_length {
164 25 100 66 25 1 4052 my $proto = @_ && UNIVERSAL::isa($_[0],__PACKAGE__) ? shift : __PACKAGE__;
165 25         44 my $source = shift;
166 25 100       39 unless (defined($source)) {
167 1         3 return $source;
168             }
169 24         25 my $ref;
170 24 100       39 if (ref($source)) {
171 18 50       27 unless (defined($$source)) {
172 0         0 return $$source;
173             }
174 18         24 $ref = $source;
175             }
176             else {
177 6         7 $ref = \$source;
178             }
179 24         25 my $min_compressed_length = 13;
180 24         135 require bytes;
181 24         46 my $len = bytes::length($$ref);
182 24 100       90 if ($len < $min_compressed_length) {
183 4 50       7 if ($len == 0) { # COMPRESS() returns an empty string when given an empty string.
184 4         7 return 0;
185             }
186 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";
187 0         0 return undef;
188             }
189 20         64 my $expect_len = unpack('V', substr($$ref,0,4));
190 20         33 return $expect_len;
191             }
192              
193              
194              
195             1;
196              
197             __END__