Commit | Line | Data |
---|---|---|
0314122a NC |
1 | #!perl -w |
2 | ||
3 | BEGIN { | |
4 | chdir 't' if -d 't'; | |
5 | @INC = '../lib'; | |
6 | push @INC, "::lib:$MacPerl::Architecture:" if $^O eq 'MacOS'; | |
7 | require Config; import Config; | |
8 | if ($Config{'extensions'} !~ /\bXS\/APItest\b/) { | |
9 | # Look, I'm using this fully-qualified variable more than once! | |
10 | my $arch = $MacPerl::Architecture; | |
11 | print "1..0 # Skip: XS::APItest was not built\n"; | |
12 | exit 0; | |
13 | } | |
14 | } | |
15 | ||
3128e575 NC |
16 | use strict; |
17 | use utf8; | |
0314122a | 18 | use Tie::Hash; |
3128e575 NC |
19 | use Test::More 'no_plan'; |
20 | ||
55289a74 | 21 | BEGIN {use_ok('XS::APItest')}; |
0314122a | 22 | |
3128e575 NC |
23 | sub preform_test; |
24 | sub test_present; | |
25 | sub test_absent; | |
26 | sub test_delete_present; | |
27 | sub test_delete_absent; | |
28 | sub brute_force_exists; | |
29 | sub test_store; | |
30 | sub test_fetch_present; | |
31 | sub test_fetch_absent; | |
0314122a | 32 | |
b60cf05a NC |
33 | my $utf8_for_258 = chr 258; |
34 | utf8::encode $utf8_for_258; | |
0314122a | 35 | |
3128e575 | 36 | my @testkeys = ('N', chr 198, chr 256); |
b60cf05a | 37 | my @keys = (@testkeys, $utf8_for_258); |
0314122a | 38 | |
3128e575 NC |
39 | foreach (@keys) { |
40 | utf8::downgrade $_, 1; | |
41 | } | |
42 | main_tests (\@keys, \@testkeys, ''); | |
0314122a | 43 | |
3128e575 NC |
44 | foreach (@keys) { |
45 | utf8::upgrade $_; | |
46 | } | |
47 | main_tests (\@keys, \@testkeys, ' [utf8 hash]'); | |
0314122a | 48 | |
3128e575 NC |
49 | { |
50 | my %h = (a=>'cheat'); | |
51 | tie %h, 'Tie::StdHash'; | |
9568a123 NC |
52 | # is bug 36327 fixed? |
53 | my $result = ($] > 5.009) ? undef : 1; | |
54 | ||
55 | is (XS::APItest::Hash::store(\%h, chr 258, 1), $result); | |
3128e575 NC |
56 | |
57 | ok (!exists $h{$utf8_for_258}, | |
58 | "hv_store doesn't insert a key with the raw utf8 on a tied hash"); | |
59 | } | |
0314122a | 60 | |
9568a123 | 61 | if ($] > 5.009) { |
5d2b1485 NC |
62 | my $strtab = strtab(); |
63 | is (ref $strtab, 'HASH', "The shared string table quacks like a hash"); | |
8ca60cef | 64 | my $wibble = "\0"; |
5d2b1485 | 65 | eval { |
8ca60cef | 66 | $strtab->{$wibble}++; |
5d2b1485 NC |
67 | }; |
68 | my $prefix = "Cannot modify shared string table in hv_"; | |
69 | my $what = $prefix . 'fetch'; | |
70 | like ($@, qr/^$what/,$what); | |
71 | eval { | |
72 | XS::APItest::Hash::store($strtab, 'Boom!', 1) | |
73 | }; | |
74 | $what = $prefix . 'store'; | |
75 | like ($@, qr/^$what/, $what); | |
76 | if (0) { | |
77 | A::B->method(); | |
78 | } | |
79 | # DESTROY should be in there. | |
80 | eval { | |
81 | delete $strtab->{DESTROY}; | |
82 | }; | |
83 | $what = $prefix . 'delete'; | |
84 | like ($@, qr/^$what/, $what); | |
85 | # I can't work out how to get to the code that flips the wasutf8 flag on | |
86 | # the hash key without some ikcy XS | |
87 | } | |
2dc92170 NC |
88 | |
89 | { | |
90 | is_deeply([&XS::APItest::Hash::test_hv_free_ent], [2,2,1,1], | |
91 | "hv_free_ent frees the value immediately"); | |
92 | is_deeply([&XS::APItest::Hash::test_hv_delayfree_ent], [2,2,2,1], | |
93 | "hv_delayfree_ent keeps the value around until FREETMPS"); | |
94 | } | |
35ab5632 NC |
95 | |
96 | foreach my $in ("", "N", "a\0b") { | |
97 | my $got = XS::APItest::Hash::test_share_unshare_pvn($in); | |
98 | is ($got, $in, "test_share_unshare_pvn"); | |
99 | } | |
100 | ||
55289a74 | 101 | if ($] > 5.009) { |
53c40a8f NC |
102 | foreach ([\&XS::APItest::Hash::rot13_hash, \&rot13, "rot 13"], |
103 | [\&XS::APItest::Hash::bitflip_hash, \&bitflip, "bitflip"], | |
104 | ) { | |
105 | my ($setup, $mapping, $name) = @$_; | |
106 | my %hash; | |
107 | my %placebo = (a => 1, p => 2, i => 4, e => 8); | |
108 | $setup->(\%hash); | |
109 | $hash{a}++; @hash{qw(p i e)} = (2, 4, 8); | |
110 | ||
111 | test_U_hash(\%hash, \%placebo, [f => 9, g => 10, h => 11], $mapping, | |
112 | $name); | |
113 | } | |
850f5f16 NC |
114 | foreach my $upgrade_o (0, 1) { |
115 | foreach my $upgrade_n (0, 1) { | |
116 | my (%hash, %placebo); | |
117 | XS::APItest::Hash::bitflip_hash(\%hash); | |
118 | foreach my $new (["7", 65, 67, 80], | |
119 | ["8", 163, 171, 215], | |
120 | ["U", 2603, 2604, 2604], | |
121 | ) { | |
122 | foreach my $code (78, 240, 256, 1336) { | |
123 | my $key = chr $code; | |
124 | # This is the UTF-8 byte sequence for the key. | |
125 | my $key_utf8 = $key; | |
126 | utf8::encode($key_utf8); | |
127 | if ($upgrade_o) { | |
128 | $key .= chr 256; | |
129 | chop $key; | |
130 | } | |
131 | $hash{$key} = $placebo{$key} = $code; | |
132 | $hash{$key_utf8} = $placebo{$key_utf8} = "$code as UTF-8"; | |
133 | } | |
134 | my $name = 'bitflip ' . shift @$new; | |
135 | my @new_kv; | |
136 | foreach my $code (@$new) { | |
137 | my $key = chr $code; | |
138 | if ($upgrade_n) { | |
139 | $key .= chr 256; | |
140 | chop $key; | |
141 | } | |
142 | push @new_kv, $key, $_; | |
143 | } | |
144 | ||
145 | $name .= ' upgraded(orig) ' if $upgrade_o; | |
146 | $name .= ' upgraded(new) ' if $upgrade_n; | |
147 | test_U_hash(\%hash, \%placebo, \@new_kv, \&bitflip, $name); | |
148 | } | |
149 | } | |
150 | } | |
53c40a8f NC |
151 | } |
152 | ||
527df579 NC |
153 | { |
154 | my $as_utf8 = "\241" . chr 256; | |
155 | chop $as_utf8; | |
156 | my $as_bytes = "\243"; | |
157 | foreach my $key ('N', $as_bytes, $as_utf8, "\x{2623}") { | |
158 | my $ord = ord $key; | |
58ca560a NC |
159 | foreach my $hash_it (0, 1) { |
160 | foreach my $what (qw(pv sv)) { | |
161 | my %hash; | |
162 | is (XS::APItest::Hash::common({hv => \%hash, | |
163 | "key$what" => $key, | |
164 | val => $ord, | |
165 | "hash_$what" => $hash_it, | |
166 | action => | |
167 | XS::APItest::HV_FETCH_ISSTORE}), | |
168 | $ord, "store $ord with $what \$hash_it = $hash_it"); | |
169 | is_deeply ([each %hash], [$key, $ord], | |
170 | "First key read is good"); | |
171 | is_deeply ([each %hash], [], "No second key good"); | |
172 | ||
173 | is ($hash{$key}, $ord, "Direct hash read finds $ord"); | |
174 | } | |
527df579 NC |
175 | } |
176 | } | |
177 | } | |
178 | ||
53c40a8f NC |
179 | exit; |
180 | ||
181 | ################################ The End ################################ | |
182 | ||
183 | sub test_U_hash { | |
184 | my ($hash, $placebo, $new, $mapping, $message) = @_; | |
185 | my @hitlist = keys %$placebo; | |
186 | print "# $message\n"; | |
b54b4831 | 187 | |
53c40a8f NC |
188 | my @keys = sort keys %$hash; |
189 | is ("@keys", join(' ', sort($mapping->(keys %$placebo))), | |
190 | "uvar magic called exactly once on store"); | |
b54b4831 | 191 | |
850f5f16 | 192 | is (keys %$hash, keys %$placebo); |
55289a74 | 193 | |
53c40a8f NC |
194 | my $victim = shift @hitlist; |
195 | is (delete $hash->{$victim}, delete $placebo->{$victim}); | |
55289a74 | 196 | |
850f5f16 | 197 | is (keys %$hash, keys %$placebo); |
53c40a8f NC |
198 | @keys = sort keys %$hash; |
199 | is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); | |
55289a74 | 200 | |
53c40a8f NC |
201 | $victim = shift @hitlist; |
202 | is (XS::APItest::Hash::delete_ent ($hash, $victim, | |
55289a74 NC |
203 | XS::APItest::HV_DISABLE_UVAR_XKEY), |
204 | undef, "Deleting a known key with conversion disabled fails (ent)"); | |
850f5f16 | 205 | is (keys %$hash, keys %$placebo); |
55289a74 | 206 | |
53c40a8f NC |
207 | is (XS::APItest::Hash::delete_ent ($hash, $victim, 0), |
208 | delete $placebo->{$victim}, | |
209 | "Deleting a known key with conversion enabled works (ent)"); | |
850f5f16 | 210 | is (keys %$hash, keys %$placebo); |
53c40a8f NC |
211 | @keys = sort keys %$hash; |
212 | is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); | |
55289a74 | 213 | |
53c40a8f NC |
214 | $victim = shift @hitlist; |
215 | is (XS::APItest::Hash::delete ($hash, $victim, | |
55289a74 NC |
216 | XS::APItest::HV_DISABLE_UVAR_XKEY), |
217 | undef, "Deleting a known key with conversion disabled fails"); | |
850f5f16 | 218 | is (keys %$hash, keys %$placebo); |
53c40a8f NC |
219 | |
220 | is (XS::APItest::Hash::delete ($hash, $victim, 0), | |
221 | delete $placebo->{$victim}, | |
222 | "Deleting a known key with conversion enabled works"); | |
850f5f16 | 223 | is (keys %$hash, keys %$placebo); |
53c40a8f NC |
224 | @keys = sort keys %$hash; |
225 | is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); | |
226 | ||
227 | my ($k, $v) = splice @$new, 0, 2; | |
228 | $hash->{$k} = $v; | |
229 | $placebo->{$k} = $v; | |
850f5f16 | 230 | is (keys %$hash, keys %$placebo); |
53c40a8f NC |
231 | @keys = sort keys %$hash; |
232 | is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); | |
233 | ||
234 | ($k, $v) = splice @$new, 0, 2; | |
235 | is (XS::APItest::Hash::store_ent($hash, $k, $v), $v, "store_ent"); | |
236 | $placebo->{$k} = $v; | |
850f5f16 | 237 | is (keys %$hash, keys %$placebo); |
53c40a8f NC |
238 | @keys = sort keys %$hash; |
239 | is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); | |
240 | ||
241 | ($k, $v) = splice @$new, 0, 2; | |
242 | is (XS::APItest::Hash::store($hash, $k, $v), $v, "store"); | |
53c40a8f | 243 | $placebo->{$k} = $v; |
850f5f16 | 244 | is (keys %$hash, keys %$placebo); |
53c40a8f NC |
245 | @keys = sort keys %$hash; |
246 | is ("@keys", join(' ', sort($mapping->(keys %$placebo)))); | |
247 | ||
248 | @hitlist = keys %$placebo; | |
249 | $victim = shift @hitlist; | |
250 | is (XS::APItest::Hash::fetch_ent($hash, $victim), $placebo->{$victim}, | |
251 | "fetch_ent"); | |
252 | is (XS::APItest::Hash::fetch_ent($hash, $mapping->($victim)), undef, | |
bdee33e4 NC |
253 | "fetch_ent (missing)"); |
254 | ||
53c40a8f NC |
255 | $victim = shift @hitlist; |
256 | is (XS::APItest::Hash::fetch($hash, $victim), $placebo->{$victim}, | |
257 | "fetch"); | |
258 | is (XS::APItest::Hash::fetch($hash, $mapping->($victim)), undef, | |
bdee33e4 NC |
259 | "fetch (missing)"); |
260 | ||
53c40a8f NC |
261 | $victim = shift @hitlist; |
262 | ok (XS::APItest::Hash::exists_ent($hash, $victim), "exists_ent"); | |
263 | ok (!XS::APItest::Hash::exists_ent($hash, $mapping->($victim)), | |
bdee33e4 NC |
264 | "exists_ent (missing)"); |
265 | ||
53c40a8f | 266 | $victim = shift @hitlist; |
6b4de907 | 267 | die "Need a victim" unless defined $victim; |
53c40a8f NC |
268 | ok (XS::APItest::Hash::exists($hash, $victim), "exists"); |
269 | ok (!XS::APItest::Hash::exists($hash, $mapping->($victim)), | |
270 | "exists (missing)"); | |
6b4de907 NC |
271 | |
272 | is (XS::APItest::Hash::common({hv => $hash, keysv => $victim}), | |
273 | $placebo->{$victim}, "common (fetch)"); | |
274 | is (XS::APItest::Hash::common({hv => $hash, keypv => $victim}), | |
275 | $placebo->{$victim}, "common (fetch pv)"); | |
276 | is (XS::APItest::Hash::common({hv => $hash, keysv => $victim, | |
277 | action => XS::APItest::HV_DISABLE_UVAR_XKEY}), | |
278 | undef, "common (fetch) missing"); | |
279 | is (XS::APItest::Hash::common({hv => $hash, keypv => $victim, | |
280 | action => XS::APItest::HV_DISABLE_UVAR_XKEY}), | |
281 | undef, "common (fetch pv) missing"); | |
282 | is (XS::APItest::Hash::common({hv => $hash, keysv => $mapping->($victim), | |
283 | action => XS::APItest::HV_DISABLE_UVAR_XKEY}), | |
284 | $placebo->{$victim}, "common (fetch) missing mapped"); | |
285 | is (XS::APItest::Hash::common({hv => $hash, keypv => $mapping->($victim), | |
286 | action => XS::APItest::HV_DISABLE_UVAR_XKEY}), | |
287 | $placebo->{$victim}, "common (fetch pv) missing mapped"); | |
b54b4831 NC |
288 | } |
289 | ||
3128e575 NC |
290 | sub main_tests { |
291 | my ($keys, $testkeys, $description) = @_; | |
292 | foreach my $key (@$testkeys) { | |
293 | my $lckey = ($key eq chr 198) ? chr 230 : lc $key; | |
294 | my $unikey = $key; | |
295 | utf8::encode $unikey; | |
0314122a | 296 | |
3128e575 NC |
297 | utf8::downgrade $key, 1; |
298 | utf8::downgrade $lckey, 1; | |
299 | utf8::downgrade $unikey, 1; | |
300 | main_test_inner ($key, $lckey, $unikey, $keys, $description); | |
0314122a | 301 | |
3128e575 NC |
302 | utf8::upgrade $key; |
303 | utf8::upgrade $lckey; | |
304 | utf8::upgrade $unikey; | |
305 | main_test_inner ($key, $lckey, $unikey, $keys, | |
306 | $description . ' [key utf8 on]'); | |
307 | } | |
0314122a | 308 | |
3128e575 NC |
309 | # hv_exists was buggy for tied hashes, in that the raw utf8 key was being |
310 | # used - the utf8 flag was being lost. | |
311 | perform_test (\&test_absent, (chr 258), $keys, ''); | |
0314122a | 312 | |
3128e575 NC |
313 | perform_test (\&test_fetch_absent, (chr 258), $keys, ''); |
314 | perform_test (\&test_delete_absent, (chr 258), $keys, ''); | |
0314122a NC |
315 | } |
316 | ||
3128e575 NC |
317 | sub main_test_inner { |
318 | my ($key, $lckey, $unikey, $keys, $description) = @_; | |
319 | perform_test (\&test_present, $key, $keys, $description); | |
320 | perform_test (\&test_fetch_present, $key, $keys, $description); | |
321 | perform_test (\&test_delete_present, $key, $keys, $description); | |
b60cf05a | 322 | |
3128e575 NC |
323 | perform_test (\&test_store, $key, $keys, $description, [a=>'cheat']); |
324 | perform_test (\&test_store, $key, $keys, $description, []); | |
b60cf05a | 325 | |
3128e575 NC |
326 | perform_test (\&test_absent, $lckey, $keys, $description); |
327 | perform_test (\&test_fetch_absent, $lckey, $keys, $description); | |
328 | perform_test (\&test_delete_absent, $lckey, $keys, $description); | |
b60cf05a | 329 | |
3128e575 NC |
330 | return if $unikey eq $key; |
331 | ||
332 | perform_test (\&test_absent, $unikey, $keys, $description); | |
333 | perform_test (\&test_fetch_absent, $unikey, $keys, $description); | |
334 | perform_test (\&test_delete_absent, $unikey, $keys, $description); | |
b60cf05a NC |
335 | } |
336 | ||
3128e575 NC |
337 | sub perform_test { |
338 | my ($test_sub, $key, $keys, $message, @other) = @_; | |
b60cf05a NC |
339 | my $printable = join ',', map {ord} split //, $key; |
340 | ||
3128e575 NC |
341 | my (%hash, %tiehash); |
342 | tie %tiehash, 'Tie::StdHash'; | |
b60cf05a | 343 | |
3128e575 NC |
344 | @hash{@$keys} = @$keys; |
345 | @tiehash{@$keys} = @$keys; | |
b60cf05a | 346 | |
3128e575 NC |
347 | &$test_sub (\%hash, $key, $printable, $message, @other); |
348 | &$test_sub (\%tiehash, $key, $printable, "$message tie", @other); | |
b60cf05a NC |
349 | } |
350 | ||
3128e575 NC |
351 | sub test_present { |
352 | my ($hash, $key, $printable, $message) = @_; | |
353 | ||
354 | ok (exists $hash->{$key}, "hv_exists_ent present$message $printable"); | |
355 | ok (XS::APItest::Hash::exists ($hash, $key), | |
356 | "hv_exists present$message $printable"); | |
b60cf05a NC |
357 | } |
358 | ||
3128e575 NC |
359 | sub test_absent { |
360 | my ($hash, $key, $printable, $message) = @_; | |
858117f8 | 361 | |
3128e575 NC |
362 | ok (!exists $hash->{$key}, "hv_exists_ent absent$message $printable"); |
363 | ok (!XS::APItest::Hash::exists ($hash, $key), | |
364 | "hv_exists absent$message $printable"); | |
b60cf05a NC |
365 | } |
366 | ||
3128e575 NC |
367 | sub test_delete_present { |
368 | my ($hash, $key, $printable, $message) = @_; | |
b60cf05a | 369 | |
3128e575 NC |
370 | my $copy = {}; |
371 | my $class = tied %$hash; | |
372 | if (defined $class) { | |
373 | tie %$copy, ref $class; | |
374 | } | |
375 | $copy = {%$hash}; | |
8829b5e2 NC |
376 | ok (brute_force_exists ($copy, $key), |
377 | "hv_delete_ent present$message $printable"); | |
3128e575 | 378 | is (delete $copy->{$key}, $key, "hv_delete_ent present$message $printable"); |
8829b5e2 NC |
379 | ok (!brute_force_exists ($copy, $key), |
380 | "hv_delete_ent present$message $printable"); | |
3128e575 | 381 | $copy = {%$hash}; |
8829b5e2 NC |
382 | ok (brute_force_exists ($copy, $key), |
383 | "hv_delete present$message $printable"); | |
3128e575 NC |
384 | is (XS::APItest::Hash::delete ($copy, $key), $key, |
385 | "hv_delete present$message $printable"); | |
8829b5e2 NC |
386 | ok (!brute_force_exists ($copy, $key), |
387 | "hv_delete present$message $printable"); | |
b60cf05a NC |
388 | } |
389 | ||
3128e575 NC |
390 | sub test_delete_absent { |
391 | my ($hash, $key, $printable, $message) = @_; | |
b60cf05a | 392 | |
3128e575 NC |
393 | my $copy = {}; |
394 | my $class = tied %$hash; | |
395 | if (defined $class) { | |
396 | tie %$copy, ref $class; | |
397 | } | |
398 | $copy = {%$hash}; | |
399 | is (delete $copy->{$key}, undef, "hv_delete_ent absent$message $printable"); | |
400 | $copy = {%$hash}; | |
401 | is (XS::APItest::Hash::delete ($copy, $key), undef, | |
402 | "hv_delete absent$message $printable"); | |
b60cf05a NC |
403 | } |
404 | ||
3128e575 NC |
405 | sub test_store { |
406 | my ($hash, $key, $printable, $message, $defaults) = @_; | |
407 | my $HV_STORE_IS_CRAZY = 1; | |
b60cf05a | 408 | |
3128e575 NC |
409 | # We are cheating - hv_store returns NULL for a store into an empty |
410 | # tied hash. This isn't helpful here. | |
0314122a | 411 | |
3128e575 | 412 | my $class = tied %$hash; |
0314122a | 413 | |
9568a123 NC |
414 | # It's important to do this with nice new hashes created each time round |
415 | # the loop, rather than hashes in the pad, which get recycled, and may have | |
416 | # xhv_array non-NULL | |
417 | my $h1 = {@$defaults}; | |
418 | my $h2 = {@$defaults}; | |
3128e575 | 419 | if (defined $class) { |
9568a123 NC |
420 | tie %$h1, ref $class; |
421 | tie %$h2, ref $class; | |
422 | if ($] > 5.009) { | |
423 | # bug 36327 is fixed | |
424 | $HV_STORE_IS_CRAZY = undef; | |
425 | } else { | |
426 | # HV store_ent returns 1 if there was already underlying hash storage | |
427 | $HV_STORE_IS_CRAZY = undef unless @$defaults; | |
428 | } | |
3128e575 | 429 | } |
9568a123 NC |
430 | is (XS::APItest::Hash::store_ent($h1, $key, 1), $HV_STORE_IS_CRAZY, |
431 | "hv_store_ent$message $printable"); | |
432 | ok (brute_force_exists ($h1, $key), "hv_store_ent$message $printable"); | |
433 | is (XS::APItest::Hash::store($h2, $key, 1), $HV_STORE_IS_CRAZY, | |
3128e575 | 434 | "hv_store$message $printable"); |
9568a123 | 435 | ok (brute_force_exists ($h2, $key), "hv_store$message $printable"); |
3128e575 | 436 | } |
0314122a | 437 | |
3128e575 NC |
438 | sub test_fetch_present { |
439 | my ($hash, $key, $printable, $message) = @_; | |
b60cf05a | 440 | |
3128e575 NC |
441 | is ($hash->{$key}, $key, "hv_fetch_ent present$message $printable"); |
442 | is (XS::APItest::Hash::fetch ($hash, $key), $key, | |
443 | "hv_fetch present$message $printable"); | |
0314122a NC |
444 | } |
445 | ||
3128e575 NC |
446 | sub test_fetch_absent { |
447 | my ($hash, $key, $printable, $message) = @_; | |
b60cf05a | 448 | |
3128e575 NC |
449 | is ($hash->{$key}, undef, "hv_fetch_ent absent$message $printable"); |
450 | is (XS::APItest::Hash::fetch ($hash, $key), undef, | |
451 | "hv_fetch absent$message $printable"); | |
452 | } | |
b60cf05a | 453 | |
3128e575 NC |
454 | sub brute_force_exists { |
455 | my ($hash, $key) = @_; | |
456 | foreach (keys %$hash) { | |
457 | return 1 if $key eq $_; | |
458 | } | |
459 | return 0; | |
b60cf05a | 460 | } |
b54b4831 NC |
461 | |
462 | sub rot13 { | |
463 | my @results = map {my $a = $_; $a =~ tr/A-Za-z/N-ZA-Mn-za-m/; $a} @_; | |
464 | wantarray ? @results : $results[0]; | |
465 | } | |
53c40a8f NC |
466 | |
467 | sub bitflip { | |
468 | my @results = map {join '', map {chr(32 ^ ord $_)} split '', $_} @_; | |
469 | wantarray ? @results : $results[0]; | |
470 | } |