Tie::CopyOnWrite - copy-on-write semantics for Perl data structures using tie
use Tie::CopyOnWrite;
$foo = "foo" x 10;
copyonwrite $foo => $bar;
# At this point, there is only one copy of the string in memory,
# accessible through the tie'd variables $foo and $bar.
print "$foo\n$bar\n" if tied $foo == tied $bar;
# Only when one of them is modified do we actually copy the data:
$bar .= "bar"; # $bar is untie'd at this point
print "$foo\n$bar\n";
If data is frequently copied but infrequently modified, it can make sense to
defer the actual copy operation until such time as one of the "copies" is
modified. Until that time, a data structure "copied" using one of the
functions in this module is actually just a reference to the original data;
that fact is hidden from its user however, who can access the data just as she
would with an ordinary copy, thanks to Perl's tie feature.
All six functions documented here are exported by default.
Both $original and $copy become tied scalars referring to the value
which was originally in the ordinary scalar $original. When (if) the value
of one of these variables is changed, it will be demoted to an ordinary
scalar; only then will the data actually be copied.
Both @original and @copy become tied arrays referring to the list
which was originally in the ordinary array @original. When (if) one of
these variables is modified (by adding, removing or changing an array
element), it will be demoted to an ordinary array; only then will the data
actually be copied.
The actual elements in the array are not tied, so modifying a nested
structure referred to by a reference contained in a tied array will cause
the same modification in its "copy." If you want to avoid that, use
deep_copyonwrite_array.
Both %original and %copy become tied hashes referring to the hash
which was originally in the ordinary variable %original. When (if) one of
these variables is modified (by adding, removing or changing a hash element),
it will be demoted to an ordinary hash; only then will the data actually be
copied.
The actual elements in the hash are not tied, so modifying a nested
structure referred to by a reference contained in a tied hash will cause
the same modification in its "copy." If you want to avoid that, use
deep_copyonwrite_hash.
Both @original and @copy are ordinary arrays, and remain ordinary
arrays after this function is called. Their scalar elements, on the other
hand, become tied scalars referring to the scalar elements taken from the
@original array.
Nested structures are handled properly, so @original could be an array of
arrays, or an array of hashes, or an array of hashes of arrays, or whatever,
and only the "leaves" of the "tree" will be tied. Any reference that is not
to a scalar, array or hash will be copied in the normal way, resulting in an
old-fashioned alias.
Both %original and %copy are ordinary hashes, and remain ordinary
hashes after this function is called. Their scalar elements, on the other
hand, become tied scalars referring to the scalar elements taken from the
%original hash.
Nested structures are handled properly, so %original could be a hash of
arrays, or a hash of hashes, or a hash of hashes of arrays, or whatever, and
only the "leaves" of the "tree" will be tied. Any reference that is not to
a scalar, array or hash will be copied in the normal way, resulting in an
old-fashioned alias.
This is a generalisation of deep_copyonwrite_array and
deep_copyonwrite_hash which expects a reference to any supported type of
structure.
$foo = "foo" x 100;
copyonwrite $foo => $bar;
$baz = $bar;
In the above code snippet, there are two real copies of the string in memory:
the one tied to $foo and $bar, and the one in the ordinary variable
$baz. The assignment operation has dropped the tie and resulted in a
plain old copy, although $foo and $bar remain tied.
This actuality can express itself in more subtle ways:
sub mycopy {
copyonwrite $original => my $copy;
return $copy; # doesn't do what you wanted
}
if (tied mycopy()) { print "this won't be printed" }
This can be worked around in several ways; for example:
sub mycopy {
copyonwrite $original => $_[0];
}
mycopy($copy);
if (tied $copy) { print "this will be printed" }
The deep-copy functions can not properly handle self-referential structures,
or an original structure that contains references to the copy structure.
Possible future extensions:
Handle more kinds of references besides scalar, array and hash.
Handle blessed references via a call back to a method defined by them.
Make the module more configurable.
Copyright 2009 the Oktalist <mat@oktalist.com>
This module is free software; you can redistribute in and/or modify it under the same terms as Perl itself.