File Coverage

blib/lib/Mojo/Util.pm
Criterion Covered Total %
statement 354 380 93.1
branch 132 154 85.7
condition 49 67 73.1
subroutine 78 80 97.5
pod 37 37 100.0
total 650 718 90.5


line stmt bran cond sub pod time code
1             package Mojo::Util;
2 98     98   97536 use Mojo::Base -strict;
  98         127  
  98         532  
3              
4 98     98   496 use Carp qw(carp croak);
  98         230  
  98         4689  
5 98     98   50767 use Data::Dumper ();
  98         681866  
  98         3165  
6 98     98   561 use Digest::MD5 qw(md5 md5_hex);
  98         140  
  98         6451  
7 98     98   42988 use Digest::SHA qw(hmac_sha1_hex sha1 sha1_hex);
  98         264981  
  98         9134  
8 98     98   46714 use Encode qw(find_encoding);
  98         1259484  
  98         7786  
9 98     98   11831 use Errno ();
  98         35438  
  98         2541  
10 98     98   365 use Exporter qw(import);
  98         149  
  98         2751  
11 98     98   382 use File::Basename qw(dirname);
  98         123  
  98         7346  
12 98     98   62616 use Getopt::Long qw(GetOptionsFromArray);
  98         977453  
  98         474  
13 98     98   65733 use IO::Compress::Gzip;
  98         2875605  
  98         5814  
14 98     98   38961 use IO::Poll qw(POLLIN POLLPRI);
  98         69461  
  98         6688  
15 98     98   48801 use IO::Uncompress::Gunzip;
  98         1230936  
  98         5361  
16 98     98   593 use List::Util qw(min);
  98         145  
  98         6421  
17 98     98   41719 use MIME::Base64 qw(decode_base64 encode_base64 encode_base64url);
  98         61548  
  98         7525  
18 98     98   626 use Mojo::BaseUtil qw(class_to_path monkey_patch);
  98         165  
  98         5170  
19 98     98   47586 use Pod::Usage qw(pod2usage);
  98         3712447  
  98         7477  
20 98     98   32081 use Socket qw(inet_pton AF_INET6 AF_INET);
  98         213011  
  98         13329  
21 98     98   596 use Symbol qw(delete_package);
  98         177  
  98         3397  
22 98     98   834 use Time::HiRes ();
  98         1048  
  98         1084  
23 98     98   43993 use Unicode::Normalize ();
  98         228258  
  98         11895  
24              
25             # Encryption support requires CryptX 0.080+
26 98 50       441 use constant CRYPTX => $ENV{MOJO_NO_CRYPTX} ? 0 : !!(eval {
27 98         19887 require CryptX;
28 0         0 require Crypt::AuthEnc::ChaCha20Poly1305;
29 0         0 require Crypt::KeyDerivation;
30 0         0 require Crypt::Misc;
31 0         0 require Crypt::PRNG;
32 0         0 CryptX->VERSION('0.080');
33 0         0 1;
34 98     98   768 });
  98         172  
35 98     98   415 use constant WIN32_API => eval { require Win32::API };
  98         145  
  98         155  
  98         16444  
36              
37             # Check for monotonic clock support
38 98     98   423 use constant MONOTONIC => !!eval { Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC()) };
  98         139  
  98         172  
  98         796  
39              
40             # Punycode bootstring parameters
41             use constant {
42 98         266759 PC_BASE => 36,
43             PC_TMIN => 1,
44             PC_TMAX => 26,
45             PC_SKEW => 38,
46             PC_DAMP => 700,
47             PC_INITIAL_BIAS => 72,
48             PC_INITIAL_N => 128
49 98     98   9318 };
  98         288  
50              
51             # To generate a new HTML entity table run this command
52             # perl examples/entities.pl > lib/Mojo/resources/html_entities.txt
53             my %ENTITIES;
54             {
55             # Don't use Mojo::File here due to circular dependencies
56             my $path = File::Spec->catfile(dirname(__FILE__), 'resources', 'html_entities.txt');
57              
58             open my $file, '<', $path or croak "Unable to open html entities file ($path): $!";
59             my $lines = do { local $/; <$file> };
60              
61             for my $line (split /\n/, $lines) {
62             next unless $line =~ /^(\S+)\s+U\+(\S+)(?:\s+U\+(\S+))?/;
63             $ENTITIES{$1} = defined $3 ? (chr(hex $2) . chr(hex $3)) : chr(hex $2);
64             }
65             }
66              
67             # Characters that should be escaped in XML
68             my %XML = ('&' => '&', '<' => '<', '>' => '>', '"' => '"', '\'' => ''');
69              
70             # "Sun, 06 Nov 1994 08:49:37 GMT" and "Sunday, 06-Nov-94 08:49:37 GMT"
71             my $EXPIRES_RE = qr/(\w+\W+\d+\W+\w+\W+\d+\W+\d+:\d+:\d+\W*\w+)/;
72              
73             # Header key/value pairs
74             my $QUOTED_VALUE_RE = qr/\G=\s*("(?:\\\\|\\"|[^"])*")/;
75             my $UNQUOTED_VALUE_RE = qr/\G=\s*([^;, ]*)/;
76              
77             # HTML entities
78             my $ENTITY_RE = qr/&(?:\#((?:[0-9]{1,7}|x[0-9a-fA-F]{1,6}));|(\w+[;=]?))/;
79              
80             # Encoding, encryption and pattern caches
81             my (%ENCODING, %ENCRYPTION, %PATTERN);
82              
83             our @EXPORT_OK = (
84             qw(b64_decode b64_encode camelize class_to_file class_to_path decamelize decode decrypt_cookie deprecated dumper),
85             qw(encode encrypt_cookie extract_usage generate_secret getopt gunzip gzip header_params hmac_sha1_sum),
86             qw(html_attr_unescape html_unescape humanize_bytes md5_bytes md5_sum monkey_patch network_contains punycode_decode),
87             qw(punycode_encode quote random_bytes scope_guard secure_compare sha1_bytes sha1_sum slugify split_cookie_header),
88             qw(split_header steady_time tablify term_escape trim unindent unquote url_escape url_unescape xml_escape xor_encode)
89             );
90              
91             # Aliases
92             monkey_patch(__PACKAGE__, 'b64_decode', \&decode_base64);
93             monkey_patch(__PACKAGE__, 'b64_encode', \&encode_base64);
94             monkey_patch(__PACKAGE__, 'hmac_sha1_sum', \&hmac_sha1_hex);
95             monkey_patch(__PACKAGE__, 'md5_bytes', \&md5);
96             monkey_patch(__PACKAGE__, 'md5_sum', \&md5_hex);
97             monkey_patch(__PACKAGE__, 'sha1_bytes', \&sha1);
98             monkey_patch(__PACKAGE__, 'sha1_sum', \&sha1_hex);
99              
100             # Use a monotonic clock if possible
101             monkey_patch(__PACKAGE__, 'steady_time',
102 166576     166576   213743 MONOTONIC ? sub () { Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC()) } : \&Time::HiRes::time);
103              
104             sub camelize {
105 42     42 1 251300 my $str = shift;
106 42 100       187 return $str if $str =~ /^[A-Z]/;
107              
108             # CamelCase words
109             return join '::', map {
110 39         170 join('', map { ucfirst lc } split /_/)
  53         144  
  81         369  
111             } split /-/, $str;
112             }
113              
114             sub class_to_file {
115 10     10 1 2698 my $class = shift;
116 10         51 $class =~ s/::|'//g;
117 10         45 $class =~ s/([A-Z])([A-Z]*)/$1 . lc $2/ge;
  18         73  
118 10         19 return decamelize($class);
119             }
120              
121             sub decamelize {
122 28     28 1 3050 my $str = shift;
123 28 100       139 return $str if $str !~ /^[A-Z]/;
124              
125             # snake_case words
126             return join '-', map {
127 23         90 join('_', map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/)
  26         102  
  49         207  
  98         114  
128             } split /::/, $str;
129             }
130              
131             sub decrypt_cookie {
132 0     0 1 0 my ($value, $key, $salt) = @_;
133 0         0 croak 'CryptX 0.080+ required for encrypted cookie support' unless CRYPTX;
134              
135 0 0       0 return undef unless $value =~ /^([^-]+)-([^-]+)-([^-]+)$/;
136 0         0 my ($ct, $iv, $tag) = ($1, $2, $3);
137 0         0 ($ct, $iv, $tag) = (Crypt::Misc::decode_b64($ct), Crypt::Misc::decode_b64($iv), Crypt::Misc::decode_b64($tag));
138              
139 0   0     0 my $dk = $ENCRYPTION{$key}{$salt} ||= Crypt::KeyDerivation::pbkdf2($key, $salt);
140 0         0 return Crypt::AuthEnc::ChaCha20Poly1305::chacha20poly1305_decrypt_verify($dk, $iv, '', $ct, $tag);
141             }
142              
143             sub decode {
144 7826     7826 1 22925 my ($encoding, $bytes) = @_;
145 7826 100       9541 return undef unless eval { $bytes = _encoding($encoding)->decode("$bytes", 1); 1 };
  7826         14548  
  7739         50953  
146 7739         18232 return $bytes;
147             }
148              
149             sub deprecated {
150 2     2 1 4483 local $Carp::CarpLevel = 1;
151 2 100       217 $ENV{MOJO_FATAL_DEPRECATIONS} ? croak @_ : carp @_;
152             }
153              
154 303     303 1 4539 sub dumper { Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Useqq(1)->Dump }
155              
156 11022     11022 1 61934 sub encode { _encoding($_[0])->encode("$_[1]", 0) }
157              
158             sub encrypt_cookie {
159 0     0 1 0 my ($value, $key, $salt) = @_;
160 0         0 croak 'CryptX 0.080+ required for encrypted cookie support' unless CRYPTX;
161              
162 0   0     0 my $dk = $ENCRYPTION{$key}{$salt} ||= Crypt::KeyDerivation::pbkdf2($key, $salt);
163 0         0 my $iv = Crypt::PRNG::random_bytes(12);
164 0         0 my ($ct, $tag) = Crypt::AuthEnc::ChaCha20Poly1305::chacha20poly1305_encrypt_authenticate($dk, $iv, '', $value);
165              
166 0         0 return join '-', Crypt::Misc::encode_b64($ct), Crypt::Misc::encode_b64($iv), Crypt::Misc::encode_b64($tag);
167             }
168              
169             sub extract_usage {
170 26 100   26 1 2886 my $file = @_ ? "$_[0]" : (caller)[1];
171              
172 26         419 open my $handle, '>', \my $output;
173 26         226 pod2usage -exitval => 'noexit', -input => $file, -output => $handle;
174 26         373622 $output =~ s/^.*\n|\n$//;
175 26         122 $output =~ s/\n$//;
176              
177 26         113 return unindent($output);
178             }
179              
180             sub generate_secret {
181 2     2 1 2720 return encode_base64url(random_bytes(128));
182             }
183              
184             sub getopt {
185 120 100   120 1 11573 my ($array, $opts) = map { ref $_[0] eq 'ARRAY' ? shift : $_ } \@ARGV, [];
  240         962  
186              
187 120         838 my $save = Getopt::Long::Configure(qw(default no_auto_abbrev no_ignore_case), @$opts);
188 120         9472 my $result = GetOptionsFromArray $array, @_;
189 120         53047 Getopt::Long::Configure($save);
190              
191 120         10130 return $result;
192             }
193              
194             sub gunzip {
195 2     2 1 1017 my $compressed = shift;
196 2 50       11 IO::Uncompress::Gunzip::gunzip \$compressed, \my $uncompressed
197             or croak "Couldn't gunzip: $IO::Uncompress::Gunzip::GzipError";
198 2         3062 return $uncompressed;
199             }
200              
201             sub gzip {
202 59     59 1 13777 my $uncompressed = shift;
203 59 50       403 IO::Compress::Gzip::gzip \$uncompressed, \my $compressed or croak "Couldn't gzip: $IO::Compress::Gzip::GzipError";
204 59         155256 return $compressed;
205             }
206              
207             sub header_params {
208 16     16 1 3072 my $value = shift;
209              
210 16         24 my $params = {};
211 16         73 while ($value =~ /\G[;\s]*([^=;, ]+)\s*/gc) {
212 20         34 my $name = $1;
213              
214             # Quoted value
215 20 100 66     110 if ($value =~ /$QUOTED_VALUE_RE/gco) { $params->{$name} //= unquote($1) }
  4 100       22  
216              
217             # Unquoted value
218 15   66     124 elsif ($value =~ /$UNQUOTED_VALUE_RE/gco) { $params->{$name} //= $1 }
219             }
220              
221 16   100     126 return ($params, substr($value, pos($value) // 0));
222             }
223              
224 34130     34130 1 36463 sub html_attr_unescape { _html(shift, 1) }
225 3226     3226 1 28568 sub html_unescape { _html(shift, 0) }
226              
227             sub humanize_bytes {
228 19     19 1 2677 my $size = shift;
229              
230 19 100       53 my $prefix = $size < 0 ? '-' : '';
231              
232 19 100       55 return "$prefix${size}B" if ($size = abs $size) < 1024;
233 16 100       48 return $prefix . _round($size) . 'KiB' if ($size /= 1024) < 1024;
234 11 100       30 return $prefix . _round($size) . 'MiB' if ($size /= 1024) < 1024;
235 8 100       26 return $prefix . _round($size) . 'GiB' if ($size /= 1024) < 1024;
236 2         20 return $prefix . _round($size /= 1024) . 'TiB';
237             }
238              
239             sub network_contains {
240 99     99 1 7630 my ($cidr, $addr) = @_;
241 99 100 100     341 return undef unless length $cidr && length $addr;
242              
243             # Parse inputs
244 93         231 my ($net, $mask) = split m!/!, $cidr, 2;
245 93         183 my $v6 = $net =~ /:/;
246 93 100 100     291 return undef if $v6 xor $addr =~ /:/;
247              
248             # Convert addresses to binary
249 91 100       350 return undef unless $net = inet_pton($v6 ? AF_INET6 : AF_INET, $net);
    100          
250 89 100       218 return undef unless $addr = inet_pton($v6 ? AF_INET6 : AF_INET, $addr);
    100          
251 87 100       148 my $length = $v6 ? 128 : 32;
252              
253             # Apply mask if given
254 87 100       295 $addr &= pack "B$length", '1' x $mask if defined $mask;
255              
256             # Compare
257 87         516 return 0 == unpack "B$length", ($net ^ $addr);
258             }
259              
260             # Direct translation of RFC 3492
261             sub punycode_decode {
262 25     25 1 3522 my $input = shift;
263 98     98   798 use integer;
  98         218  
  98         759  
264              
265 25         47 my ($n, $i, $bias, @output) = (PC_INITIAL_N, 0, PC_INITIAL_BIAS);
266              
267             # Consume all code points before the last delimiter
268 25 100       175 push @output, split(//, $1) if $input =~ s/(.*)\x2d//s;
269              
270 25         58 while (length $input) {
271 223         261 my ($oldi, $w) = ($i, 1);
272              
273             # Base to infinity in steps of base
274 223         215 for (my $k = PC_BASE; 1; $k += PC_BASE) {
275 472         440 my $digit = ord substr $input, 0, 1, '';
276 472 100       518 $digit = $digit < 0x40 ? $digit + (26 - 0x30) : ($digit & 0x1f) - 1;
277 472         422 $i += $digit * $w;
278 472         414 my $t = $k - $bias;
279 472 100       518 $t = $t < PC_TMIN ? PC_TMIN : $t > PC_TMAX ? PC_TMAX : $t;
    100          
280 472 100       582 last if $digit < $t;
281 249         267 $w *= PC_BASE - $t;
282             }
283              
284 223         320 $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
285 223         241 $n += $i / (@output + 1);
286 223         213 $i = $i % (@output + 1);
287 223         404 splice @output, $i++, 0, chr $n;
288             }
289              
290 25         151 return join '', @output;
291             }
292              
293             # Direct translation of RFC 3492
294             sub punycode_encode {
295 66     66 1 21695 my $output = shift;
296 98     98   31643 use integer;
  98         187  
  98         380  
297              
298 66         132 my ($n, $delta, $bias) = (PC_INITIAL_N, 0, PC_INITIAL_BIAS);
299              
300             # Extract basic code points
301 66         221 my @input = map {ord} split //, $output;
  558         669  
302 66         353 $output =~ s/[^\x00-\x7f]+//gs;
303 66         168 my $h = my $basic = length $output;
304 66 100       181 $output .= "\x2d" if $basic > 0;
305              
306 66         104 for my $m (sort { $a <=> $b } grep { $_ >= PC_INITIAL_N } @input) {
  613         528  
  558         766  
307 264 100       408 next if $m < $n;
308 222         239 $delta += ($m - $n) * ($h + 1);
309 222         219 $n = $m;
310              
311 222         250 for my $c (@input) {
312              
313 3643 100       3932 if ($c < $n) { $delta++ }
  2039 100       1805  
314             elsif ($c == $n) {
315 264         239 my $q = $delta;
316              
317             # Base to infinity in steps of base
318 264         252 for (my $k = PC_BASE; 1; $k += PC_BASE) {
319 595         549 my $t = $k - $bias;
320 595 100       714 $t = $t < PC_TMIN ? PC_TMIN : $t > PC_TMAX ? PC_TMAX : $t;
    100          
321 595 100       754 last if $q < $t;
322 331         337 my $o = $t + (($q - $t) % (PC_BASE - $t));
323 331 100       514 $output .= chr $o + ($o < 26 ? 0x61 : 0x30 - 26);
324 331         370 $q = ($q - $t) / (PC_BASE - $t);
325             }
326              
327 264 50       352 $output .= chr $q + ($q < 26 ? 0x61 : 0x30 - 26);
328 264         364 $bias = _adapt($delta, $h + 1, $h == $basic);
329 264         259 $delta = 0;
330 264         274 $h++;
331             }
332             }
333              
334 222         223 $delta++;
335 222         257 $n++;
336             }
337              
338 66         284 return $output;
339             }
340              
341             sub quote {
342 21     21 1 4336 my $str = shift;
343 21         105 $str =~ s/(["\\])/\\$1/g;
344 21         89 return qq{"$str"};
345             }
346              
347             sub random_bytes {
348 6     6 1 5614 my ($count) = @_;
349 6 50 33     55 croak 'Byte count must be a defined, non-negative integer' unless defined $count && $count =~ /\A\d+\z/a;
350 6 50       149 if (CRYPTX) {
    50          
351             return Crypt::PRNG::random_bytes($count);
352             }
353 0         0 elsif (WIN32_API) {
354 0 0       0 state $genrand = Win32::API->new('advapi32', 'INT SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength)')
355             or die "Could not import SystemFunction036: $^E";
356 0 0       0 return '' if $count == 0;
357 0         0 my $buffer = chr(0) x $count;
358 0 0       0 $genrand->Call($buffer, $count) or croak("Could not read random bytes");
359 0         0 return $buffer;
360             }
361             elsif (-e '/dev/urandom') {
362 6 50       210 open my $fh, '<:raw', '/dev/urandom' or die "Couldn't open /dev/urandom: $!";
363 6         19 my ($result, $offset) = ('', 0);
364 6         15 while ($offset < $count) {
365 6         39 my $read = sysread $fh, $result, $count - $offset, $offset;
366 6 0 33     16 next if not defined $read and $!{EINTR};
367 6 50 33     26 croak("Could not read random bytes") if not defined $read or $read == 0;
368 6         12 $offset += $read;
369             }
370 6         106 return $result;
371             }
372             else {
373 0         0 die "Can't find source of randomness on your system, consider installing CryptX";
374             }
375             }
376              
377 18     18 1 2961 sub scope_guard { Mojo::Util::_Guard->new(cb => shift) }
378              
379             sub secure_compare {
380 97     97 1 13177 my ($one, $two) = @_;
381 97         159 my $r = length $one != length $two;
382 97 100       203 $two = $one if $r;
383 97         1460 $r |= ord(substr $one, $_) ^ ord(substr $two, $_) for 0 .. length($one) - 1;
384 97         363 return $r == 0;
385             }
386              
387             sub slugify {
388 14     14 1 2749 my ($value, $allow_unicode) = @_;
389              
390 14 100       30 if ($allow_unicode) {
391              
392             # Force unicode semantics by upgrading string
393 6         94 utf8::upgrade($value = Unicode::Normalize::NFKC($value));
394 6         31 $value =~ s/[^\w\s-]+//g;
395             }
396             else {
397 8         198 $value = Unicode::Normalize::NFKD($value);
398 8         64 $value =~ s/[^a-zA-Z0-9_\p{PosixSpace}-]+//g;
399             }
400 14         31 (my $new = lc trim($value)) =~ s/[-\s]+/-/g;
401              
402 14         60 return $new;
403             }
404              
405 1072     1072 1 6684 sub split_cookie_header { _header(shift, 1) }
406 225     225 1 3362 sub split_header { _header(shift, 0) }
407              
408             sub tablify {
409 18     18 1 4911 my $rows = shift;
410              
411 18         27 my @spec;
412 18         30 for my $row (@$rows) {
413 87         138 for my $i (0 .. $#$row) {
414 176   100     279 ($row->[$i] //= '') =~ y/\r\n//d;
415 176         183 my $len = length $row->[$i];
416 176 100 100     375 $spec[$i] = $len if $len >= ($spec[$i] // 0);
417             }
418             }
419              
420 18         47 my @fm = (map({"\%-${_}s"} @spec[0 .. $#spec - 1]), '%s');
  23         72  
421 18         34 return join '', map { sprintf join(' ', @fm[0 .. $#$_]) . "\n", @$_ } @$rows;
  87         316  
422             }
423              
424             sub term_escape {
425 4     4 1 2735 my $str = shift;
426 4         21 $str =~ s/([\x00-\x09\x0b-\x1f\x7f\x80-\x9f])/sprintf '\\x%02x', ord $1/ge;
  16         43  
427 4         15 return $str;
428             }
429              
430             sub trim {
431 1406     1406 1 4842 my $str = shift;
432 1406         4561 $str =~ s/^\s+//;
433 1406         3287 $str =~ s/\s+$//;
434 1406         4750 return $str;
435             }
436              
437             sub unindent {
438 37     37 1 3146 my $str = shift;
439 37 100       204 my $min = min map { m/^([ \t]*)/; length $1 || () } split /\n/, $str;
  426         575  
  426         822  
440 37 100       753 $str =~ s/^[ \t]{0,$min}//gm if $min;
441 37         869 return $str;
442             }
443              
444             sub unquote {
445 48     48 1 2689 my $str = shift;
446 48 50       208 return $str unless $str =~ s/^"(.*)"$/$1/g;
447 48         95 $str =~ s/\\\\/\\/g;
448 48         84 $str =~ s/\\"/"/g;
449 48         105 return $str;
450             }
451              
452             sub url_escape {
453 6302     6302 1 17055 my ($str, $pattern) = @_;
454              
455 6302 100       8410 if ($pattern) {
456 6287 100       11234 unless (exists $PATTERN{$pattern}) {
457 142         1363 (my $quoted = $pattern) =~ s!([/\$\[])!\\$1!g;
458 142 50       31902 $PATTERN{$pattern} = eval "sub { \$_[0] =~ s/([$quoted])/sprintf '%%%02X', ord \$1/ge }" or croak $@;
459             }
460 6287         118645 $PATTERN{$pattern}->($str);
461             }
462 15         76 else { $str =~ s/([^A-Za-z0-9\-._~])/sprintf '%%%02X', ord $1/ge }
  22         87  
463              
464 6302         17584 return $str;
465             }
466              
467             sub url_unescape {
468 8145     8145 1 16827 my $str = shift;
469 8145         13369 $str =~ s/%([0-9a-fA-F]{2})/chr hex $1/ge;
  793         2083  
470 8145         13338 return $str;
471             }
472              
473             sub xml_escape {
474 494 100 66 494 1 10844 return $_[0] if ref $_[0] && ref $_[0] eq 'Mojo::ByteStream';
  8715 100 100 8715   54103  
475 493   50     730 my $str = shift // '';
  6705   100     9232  
476 493         829 $str =~ s/([&<>"'])/$XML{$1}/ge;
  49         146  
  6705         9707  
  7644         14117  
477 493         1290 return $str;
  6705         11154  
478             }
479              
480             sub xor_encode {
481 295     295 1 3440 my ($input, $key) = @_;
482              
483             # Encode with variable key length
484 295         352 my $len = length $key;
485 295         453 my $buffer = my $output = '';
486 295         1357 $output .= $buffer ^ $key while length($buffer = substr($input, 0, $len, '')) == $len;
487 295         1312 return $output .= $buffer ^ substr($key, 0, length $buffer, '');
488             }
489              
490             sub _adapt {
491 487     487   555 my ($delta, $numpoints, $firsttime) = @_;
492 98     98   241214 use integer;
  98         184  
  98         408  
493              
494 487 100       568 $delta = $firsttime ? $delta / PC_DAMP : $delta / 2;
495 487         495 $delta += $delta / $numpoints;
496 487         425 my $k = 0;
497 487         611 while ($delta > ((PC_BASE - PC_TMIN) * PC_TMAX) / 2) {
498 116         100 $delta /= PC_BASE - PC_TMIN;
499 116         130 $k += PC_BASE;
500             }
501              
502 487         581 return $k + (((PC_BASE - PC_TMIN + 1) * $delta) / ($delta + PC_SKEW));
503             }
504              
505 18848   66 18848   117407 sub _encoding { $ENCODING{$_[0]} //= find_encoding($_[0]) // croak "Unknown encoding '$_[0]'" }
      66        
506              
507             sub _entity {
508 1003     1003   1523 my ($point, $name, $attr) = @_;
509              
510             # Code point
511 1003 100       1414 return chr($point !~ /^x/ ? $point : hex $point) unless defined $name;
    100          
512              
513             # Named character reference
514 962         913 my $rest = my $last = '';
515 962         1146 while (length $name) {
516             return $ENTITIES{$name} . reverse $rest
517 1000 100 100     3354 if exists $ENTITIES{$name} && (!$attr || $name =~ /;$/ || $last !~ /[A-Za-z0-9=]/);
      100        
518 48         82 $rest .= $last = chop $name;
519             }
520 10         44 return '&' . reverse $rest;
521             }
522              
523             sub _header {
524 1297     1297   2430 my ($str, $cookie) = @_;
525              
526 1297         1873 my (@tree, @part);
527 1297         4091 while ($str =~ /\G[,;\s]*([^=;, ]+)\s*/gc) {
528 833         1647 push @part, $1, undef;
529 833   100     2327 my $expires = $cookie && @part > 2 && lc $1 eq 'expires';
530              
531             # Special "expires" value
532 833 100 100     4126 if ($expires && $str =~ /\G=\s*$EXPIRES_RE/gco) { $part[-1] = $1 }
  125 100       223  
    100          
533              
534             # Quoted value
535 42         73 elsif ($str =~ /$QUOTED_VALUE_RE/gco) { $part[-1] = unquote $1 }
536              
537             # Unquoted value
538 563         855 elsif ($str =~ /$UNQUOTED_VALUE_RE/gco) { $part[-1] = $1 }
539              
540             # Separator
541 833 100       2190 next unless $str =~ /\G[;\s]*,\s*/gc;
542 107         281 push @tree, [@part];
543 107         283 @part = ();
544             }
545              
546             # Take care of final part
547 1297 100       4251 return [@part ? (@tree, \@part) : @tree];
548             }
549              
550             sub _html {
551 37356     37356   35870 my ($str, $attr) = @_;
552 37356         33707 $str =~ s/$ENTITY_RE/_entity($1, $2, $attr)/geo;
  1003         1092  
553 37356         88213 return $str;
554             }
555              
556             sub _options {
557              
558             # Hash or name (one)
559 1817 100   1817   4498 return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1;
  1012 100       2488  
560              
561             # Name and values (odd)
562 349 100       956 return shift, @_ if @_ % 2;
563              
564             # Name and hash or just values (even)
565 269 100       1164 return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_);
  18         78  
566             }
567              
568             # This may break in the future, but is worth it for performance
569 796     796   9798 sub _readable { !!(IO::Poll::_poll(@_[0, 1], my $m = POLLIN | POLLPRI) > 0) }
570              
571 16 100   16   155 sub _round { $_[0] < 10 ? int($_[0] * 10 + 0.5) / 10 : int($_[0] + 0.5) }
572              
573             sub _stash {
574 22420     22420   31055 my ($name, $object) = (shift, shift);
575              
576             # Hash
577 22420 100 100     99597 return $object->{$name} //= {} unless @_;
578              
579             # Get
580 1406 100 100     4677 return $object->{$name}{$_[0]} unless @_ > 1 || ref $_[0];
581              
582             # Set
583 1166 100       3813 my $values = ref $_[0] ? $_[0] : {@_};
584 1166         3016 @{$object->{$name}}{keys %$values} = values %$values;
  1166         2892  
585              
586 1166         3962 return $object;
587             }
588              
589             sub _teardown {
590 826 50   826   22978 return unless my $class = shift;
591              
592             # @ISA has to be cleared first because of circular references
593 98     98   116870 no strict 'refs';
  98         309  
  98         10965  
594 826         784 @{"${class}::ISA"} = ();
  826         6223  
595 826         1300 delete_package $class;
596             }
597              
598             package Mojo::Util::_Guard;
599 98     98   516 use Mojo::Base -base;
  98         140  
  98         801  
600              
601 18     18   1041 sub DESTROY { shift->{cb}() }
602              
603             1;
604              
605             =encoding utf8
606              
607             =head1 NAME
608              
609             Mojo::Util - Portable utility functions
610              
611             =head1 SYNOPSIS
612              
613             use Mojo::Util qw(b64_encode url_escape url_unescape);
614              
615             my $str = 'test=23';
616             my $escaped = url_escape $str;
617             say url_unescape $escaped;
618             say b64_encode $escaped, '';
619              
620             =head1 DESCRIPTION
621              
622             L provides portable utility functions for L.
623              
624             =head1 FUNCTIONS
625              
626             L implements the following functions, which can be imported individually.
627              
628             =head2 b64_decode
629              
630             my $bytes = b64_decode $b64;
631              
632             Base64 decode bytes with L.
633              
634             =head2 b64_encode
635              
636             my $b64 = b64_encode $bytes;
637             my $b64 = b64_encode $bytes, "\n";
638              
639             Base64 encode bytes with L, the line ending defaults to a newline.
640              
641             =head2 camelize
642              
643             my $camelcase = camelize $snakecase;
644              
645             Convert C string to C and replace C<-> with C<::>.
646              
647             # "FooBar"
648             camelize 'foo_bar';
649              
650             # "FooBar::Baz"
651             camelize 'foo_bar-baz';
652              
653             # "FooBar::Baz"
654             camelize 'FooBar::Baz';
655              
656             =head2 class_to_file
657              
658             my $file = class_to_file 'Foo::Bar';
659              
660             Convert a class name to a file.
661              
662             # "foo_bar"
663             class_to_file 'Foo::Bar';
664              
665             # "foobar"
666             class_to_file 'FOO::Bar';
667              
668             # "foo_bar"
669             class_to_file 'FooBar';
670              
671             # "foobar"
672             class_to_file 'FOOBar';
673              
674             =head2 class_to_path
675              
676             my $path = class_to_path 'Foo::Bar';
677              
678             Convert class name to path, as used by C<%INC>.
679              
680             # "Foo/Bar.pm"
681             class_to_path 'Foo::Bar';
682              
683             # "FooBar.pm"
684             class_to_path 'FooBar';
685              
686             =head2 decamelize
687              
688             my $snakecase = decamelize $camelcase;
689              
690             Convert C string to C and replace C<::> with C<->.
691              
692             # "foo_bar"
693             decamelize 'FooBar';
694              
695             # "foo_bar-baz"
696             decamelize 'FooBar::Baz';
697              
698             # "foo_bar-baz"
699             decamelize 'foo_bar-baz';
700              
701             =head2 decode
702              
703             my $chars = decode 'UTF-8', $bytes;
704              
705             Decode bytes to characters with L, or return C if decoding failed.
706              
707             =head2 decrypt_cookie
708              
709             my $value = decrypt_cookie $encrypted, 'passw0rd', 'salt';
710              
711             Decrypt cookie value encrypted with L, returns the decrypted value or C.
712              
713             =head2 deprecated
714              
715             deprecated 'foo is DEPRECATED in favor of bar';
716              
717             Warn about deprecated feature from perspective of caller. You can also set the C environment
718             variable to make them die instead with L.
719              
720             =head2 dumper
721              
722             my $perl = dumper {some => 'data'};
723              
724             Dump a Perl data structure with L.
725              
726             =head2 encode
727              
728             my $bytes = encode 'UTF-8', $chars;
729              
730             Encode characters to bytes with L.
731              
732             =head2 encrypt_cookie
733              
734             my $encrypted = encrypt_cookie $value, 'passw0rd', 'salt';
735              
736             Encrypt cookie value.
737              
738             =head2 extract_usage
739              
740             my $usage = extract_usage;
741             my $usage = extract_usage '/home/sri/foo.pod';
742              
743             Extract usage message from the SYNOPSIS section of a file containing POD documentation, defaults to using the file this
744             function was called from.
745              
746             # "Usage: APPLICATION test [OPTIONS]\n"
747             extract_usage;
748              
749             =head1 SYNOPSIS
750              
751             Usage: APPLICATION test [OPTIONS]
752              
753             =cut
754              
755             =head2 generate_secret
756              
757             my $secret = generate_secret;
758              
759             Generate a random secret with a cryptographically secure random number generator.
760              
761             =head2 getopt
762              
763             getopt
764             'H|headers=s' => \my @headers,
765             't|timeout=i' => \my $timeout,
766             'v|verbose' => \my $verbose;
767             getopt $array,
768             'H|headers=s' => \my @headers,
769             't|timeout=i' => \my $timeout,
770             'v|verbose' => \my $verbose;
771             getopt $array, ['pass_through'],
772             'H|headers=s' => \my @headers,
773             't|timeout=i' => \my $timeout,
774             'v|verbose' => \my $verbose;
775              
776             Extract options from an array reference with L, but without changing its global configuration, defaults
777             to using C<@ARGV>. The configuration options C and C are enabled by default.
778              
779             # Extract "charset" option
780             getopt ['--charset', 'UTF-8'], 'charset=s' => \my $charset;
781             say $charset;
782              
783             =head2 gunzip
784              
785             my $uncompressed = gunzip $compressed;
786              
787             Uncompress bytes with L.
788              
789             =head2 gzip
790              
791             my $compressed = gzip $uncompressed;
792              
793             Compress bytes with L.
794              
795             =head2 header_params
796              
797             my ($params, $remainder) = header_params 'one=foo; two="bar", three=baz';
798              
799             Extract HTTP header field parameters until the first comma according to L.
800              
801             =head2 hmac_sha1_sum
802              
803             my $checksum = hmac_sha1_sum $bytes, 'passw0rd';
804              
805             Generate HMAC-SHA1 checksum for bytes with L.
806              
807             # "11cedfd5ec11adc0ec234466d8a0f2a83736aa68"
808             hmac_sha1_sum 'foo', 'passw0rd';
809              
810             =head2 html_attr_unescape
811              
812             my $str = html_attr_unescape $escaped;
813              
814             Same as L, but handles special rules from the L
815             for HTML attributes.
816              
817             # "foo=bar<est=baz"
818             html_attr_unescape 'foo=bar<est=baz';
819              
820             # "foo=bar
821             html_attr_unescape 'foo=bar<est=baz';
822              
823             =head2 html_unescape
824              
825             my $str = html_unescape $escaped;
826              
827             Unescape all HTML entities in string.
828              
829             # "
"
830             html_unescape '<div>';
831              
832             =head2 humanize_bytes
833              
834             my $str = humanize_bytes 1234;
835              
836             Turn number of bytes into a simplified human readable format.
837              
838             # "1B"
839             humanize_bytes 1;
840              
841             # "7.5GiB"
842             humanize_bytes 8007188480;
843              
844             # "13GiB"
845             humanize_bytes 13443399680;
846              
847             # "-685MiB"
848             humanize_bytes -717946880;
849              
850             =head2 md5_bytes
851              
852             my $checksum = md5_bytes $bytes;
853              
854             Generate binary MD5 checksum for bytes with L.
855              
856             =head2 md5_sum
857              
858             my $checksum = md5_sum $bytes;
859              
860             Generate MD5 checksum for bytes with L.
861              
862             # "acbd18db4cc2f85cedef654fccc4a4d8"
863             md5_sum 'foo';
864              
865             =head2 monkey_patch
866              
867             monkey_patch $package, foo => sub {...};
868             monkey_patch $package, foo => sub {...}, bar => sub {...};
869              
870             Monkey patch functions into package.
871              
872             monkey_patch 'MyApp',
873             one => sub { say 'One!' },
874             two => sub { say 'Two!' },
875             three => sub { say 'Three!' };
876              
877             =head2 network_contains
878              
879             my $bool = network_contains $network, $address;
880              
881             Check that a given address is contained within a network in CIDR form. If the network is a single address, the
882             addresses must be equivalent.
883              
884             # True
885             network_contains('10.0.0.0/8', '10.10.10.10');
886             network_contains('10.10.10.10', '10.10.10.10');
887             network_contains('fc00::/7', 'fc::c0:ff:ee');
888              
889             # False
890             network_contains('10.0.0.0/29', '10.10.10.10');
891             network_contains('10.10.10.12', '10.10.10.10');
892             network_contains('fc00::/7', '::1');
893            
894             =head2 punycode_decode
895              
896             my $str = punycode_decode $punycode;
897              
898             Punycode decode string as described in L.
899              
900             # "bücher"
901             punycode_decode 'bcher-kva';
902              
903             =head2 punycode_encode
904              
905             my $punycode = punycode_encode $str;
906              
907             Punycode encode string as described in L.
908              
909             # "bcher-kva"
910             punycode_encode 'bücher';
911              
912             =head2 quote
913              
914             my $quoted = quote $str;
915              
916             Quote string.
917              
918             =head2 random_bytes
919              
920             my $random = random_bytes($bytes);
921              
922             Generate C<$bytes> worth of high entropy randomness.
923              
924             =head2 scope_guard
925              
926             my $guard = scope_guard sub {...};
927              
928             Create anonymous scope guard object that will execute the passed callback when the object is destroyed.
929              
930             # Execute closure at end of scope
931             {
932             my $guard = scope_guard sub { say "Mojo!" };
933             say "Hello";
934             }
935              
936             =head2 secure_compare
937              
938             my $bool = secure_compare $str1, $str2;
939              
940             Constant time comparison algorithm to prevent timing attacks. The secret string should be the second argument, to avoid
941             leaking information about the length of the string.
942              
943             =head2 sha1_bytes
944              
945             my $checksum = sha1_bytes $bytes;
946              
947             Generate binary SHA1 checksum for bytes with L.
948              
949             =head2 sha1_sum
950              
951             my $checksum = sha1_sum $bytes;
952              
953             Generate SHA1 checksum for bytes with L.
954              
955             # "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
956             sha1_sum 'foo';
957              
958             =head2 slugify
959              
960             my $slug = slugify $string;
961             my $slug = slugify $string, $bool;
962              
963             Returns a URL slug generated from the input string. Non-word characters are removed, the string is trimmed and
964             lowercased, and whitespace characters are replaced by a dash. By default, non-ASCII characters are normalized to ASCII
965             word characters or removed, but if a true value is passed as the second parameter, all word characters will be allowed
966             in the result according to unicode semantics.
967              
968             # "joel-is-a-slug"
969             slugify 'Joel is a slug';
970              
971             # "this-is-my-resume"
972             slugify 'This is: my - résumé! ☃ ';
973              
974             # "this-is-my-résumé"
975             slugify 'This is: my - résumé! ☃ ', 1;
976              
977             =head2 split_cookie_header
978              
979             my $tree = split_cookie_header 'a=b; expires=Thu, 07 Aug 2008 07:07:59 GMT';
980              
981             Same as L, but handles C values from L.
982              
983             =head2 split_header
984              
985             my $tree = split_header 'foo="bar baz"; test=123, yada';
986              
987             Split HTTP header value into key/value pairs, each comma separated part gets its own array reference, and keys without
988             a value get C assigned.
989              
990             # "one"
991             split_header('one; two="three four", five=six')->[0][0];
992              
993             # "two"
994             split_header('one; two="three four", five=six')->[0][2];
995              
996             # "three four"
997             split_header('one; two="three four", five=six')->[0][3];
998              
999             # "five"
1000             split_header('one; two="three four", five=six')->[1][0];
1001              
1002             # "six"
1003             split_header('one; two="three four", five=six')->[1][1];
1004              
1005             =head2 steady_time
1006              
1007             my $time = steady_time;
1008              
1009             High resolution time elapsed from an arbitrary fixed point in the past, resilient to time jumps if a monotonic clock is
1010             available through L.
1011              
1012             =head2 tablify
1013              
1014             my $table = tablify [['foo', 'bar'], ['baz', 'yada']];
1015              
1016             Row-oriented generator for text tables.
1017              
1018             # "foo bar\nyada yada\nbaz yada\n"
1019             tablify [['foo', 'bar'], ['yada', 'yada'], ['baz', 'yada']];
1020              
1021             =head2 term_escape
1022              
1023             my $escaped = term_escape $str;
1024              
1025             Escape all POSIX control characters except for C<\n>.
1026              
1027             # "foo\\x09bar\\x0d\n"
1028             term_escape "foo\tbar\r\n";
1029              
1030             =head2 trim
1031              
1032             my $trimmed = trim $str;
1033              
1034             Trim whitespace characters from both ends of string.
1035              
1036             # "foo bar"
1037             trim ' foo bar ';
1038              
1039             =head2 unindent
1040              
1041             my $unindented = unindent $str;
1042              
1043             Unindent multi-line string.
1044              
1045             # "foo\nbar\nbaz\n"
1046             unindent " foo\n bar\n baz\n";
1047              
1048             =head2 unquote
1049              
1050             my $str = unquote $quoted;
1051              
1052             Unquote string.
1053              
1054             =head2 url_escape
1055              
1056             my $escaped = url_escape $str;
1057             my $escaped = url_escape $str, '^A-Za-z0-9\-._~';
1058              
1059             Percent encode unsafe characters in string as described in L, the pattern
1060             used defaults to C<^A-Za-z0-9\-._~>.
1061              
1062             # "foo%3Bbar"
1063             url_escape 'foo;bar';
1064              
1065             =head2 url_unescape
1066              
1067             my $str = url_unescape $escaped;
1068              
1069             Decode percent encoded characters in string as described in L.
1070              
1071             # "foo;bar"
1072             url_unescape 'foo%3Bbar';
1073              
1074             =head2 xml_escape
1075              
1076             my $escaped = xml_escape $str;
1077              
1078             Escape unsafe characters C<&>, C>, C>, C<"> and C<'> in string, but do not escape L
1079             objects.
1080              
1081             # "<div>"
1082             xml_escape '
';
1083              
1084             # "
"
1085             use Mojo::ByteStream qw(b);
1086             xml_escape b('
');
1087              
1088             =head2 xor_encode
1089              
1090             my $encoded = xor_encode $str, $key;
1091              
1092             XOR encode string with variable length key.
1093              
1094             =head1 SEE ALSO
1095              
1096             L, L, L.
1097              
1098             =cut