NAME
Scalar::ValueTags - Attach propagated metadata to data values
SYNOPSIS
use Scalar::ValueTags;
# register value tags type with unique-hash behavior
my $vt_type = register_value_tags_type(SVTAGS_UNIQUE_HASH);
# add value tag to $foo variable;
my $tag = 'origin: somewhere';
my $foo = 32;
add_value_tag( $vt_type, \$foo, $tag );
my $tags = get_value_tags( $vt_type, \$foo );
# returns { 'origin: somewhere' => true }
# value tags are propagated along with data value
my $bar = 10;
add_value_tag( $vt_type, \$bar, "origin: elsewhere" );
my $baz = $foo + $bar;
my $tags = get_value_tags( $vt_type, \$baz );
# returns { 'origin: elsewhere' => true, 'origin: somewhere' => true }
# delete all value tags
clear_value_tags( $vt_type, \$baz ):
my $tags = get_value_tags( $vt_type, \$baz );
# returns {}
DESCRIPTION
The Scalar::ValueTags module provides functions for managing "value tags": metadata that is set on a data value and propagated from that variable to any other variable whose value is set from that of the tagged variable.
Overview
This module is similar to Variable::Magic, but applies metadata onto the data value contained within the variable rather than to the variable itself.
Every time a value is assigned to another variable, all of the value tags from all input variables are merged into the derived variable.
There are multiple "Behaviors", each of which defines what data types can be used as a value tag, and how the value tags are merged into the derived variables.
Typical Usage
Typically, a value tag contains either an indication of the source of the data or an indication of what operations are allowed or denied on the data.
If the value tags are set whenever data is received from an external source, then the value tags represent the flow of the data within the system. When the data is sent to an external destination, the value tags on that data can be used to report the data flow or to apply access control logic that depends on the source of the data.
Perl Requirements
Scalar::ValueTags depends on the scalar value magic that is being added to the Perl core as part of Magic v2.
Value Tags Types
Each client must register with Scalar::ValueTags, specifying the desired Behavior. Registering will return a unique opaque token that is then used in all functions that access value tags. The access functions will only see the value tags that were set using the same Value Tags Type.
my $vt_type = register_value_tags_type(SVTAGS_UNIQUE_REF_ARRAY);
Behaviors
A ValueTags Behavior defines the data structure used to store the value tags and the process used to merge the value tags from multiple variables. The desired Behavior must be specified when registering a Value Tags Type.
There are four Behaviors available from Scalar::ValueTags:
SVTAGS_UNIQUE_HASH
This behavior uses a hash to track each unique string tag that has been seen. All value tags must be strings. The value tags are stored in a hash, with the tags as keys and true as the value.
When merging value tags, all tags that were set in any of the source variables will be set in the destination variable.
"remove_value_tag" will remove the given tag from the hash.
SVTAGS_HASH_COUNT
This behavior uses a hash to track the number of times each string tag has been seen. All value tags must be strings. The value tags are stored in a hash, with the tags as keys and the number of times that tag has been set as the value.
When merging value tags, the tag counts from the tags of all source variables will be summed into the corresponding hash entries in the destination variable.
"remove_value_tag" will remove the given tag from the hash, along with its count.
SVTAGS_APPEND_ARRAY
This behavior uses an array to track all tags that were seen. A value tag may be any Perl variable, either scalar or reference. The value tags are stored in an array, and new tags are appended onto the end of the array.
When merging value tags, the tags of all source variables will be appended into the tags in the destination variable. The ordering of the appended array is not deterministic.
"remove_value_tag" will remove every instance of the given tag from the array.
SVTAGS_UNIQUE_REF_ARRAY
This behavior uses an array to track the unique reference address of all tags that were seen. A value tag must be a Perl reference, typically to an array or a hash. The value tags are handled as a logical set, de-duplicated by the reference address of each tag.
When merging value tags, the destination variable receives all unique reference addresses from the tags of all source variables.
"remove_value_tag" will remove the instance of the given tag from the array.
Guidelines
Behavior Choice
SVTAGS_UNIQUE_HASH
This behavior has the best merging, and only strings can be used as value tags.
Structured data can be serialized into the string value tags, if needed. The serialization must be canonical, so that there is a unique string representation for any given data structure.
add_value_tag( $vt_type, \$var, encode_json( { ... } ) );Since the tags are stored as hash keys, identical tags share the same memory.
Use this behavior if you have string or serializable tags, and you only need to know which tags have been seen in the dataflow.
SVTAGS_HASH_COUNT
This behavior has slightly lower merging performance than
SVTAGS_UNIQUE_HASH, and only strings can be used as value tags.Structured data can be serialized into the string value tags, if needed. The serialization must be canonical, so that there is a unique string representation for any given data structure.
add_value_tag( $vt_type, \$var, encode_json( { ... } ) );Since the tags are stored as hash keys, identical tags share the same memory.
Use this behavior if you have string or serializable tags, and you need to know how many times each tag has been seen in the dataflow.
SVTAGS_APPEND_ARRAY
This behavior has similar merging performance to
SVTAGS_UNIQUE_HASH, but allows any Perl scalar, arrayref, or hashref to be used as value tags.Since the tags are simply appended to an array, the size of the value tags structure increases every time a new value is derived from multiple tagged values.
Use this behavior if you have arbitrary data structures as tags, but the code using the tagged values rarely combines tagged values.
SVTAGS_UNIQUE_REF_ARRAY
This behavior has lower performance than
SVTAGS_HASH_COUNTandSVTAGS_APPEND_ARRAY, because it does a linear scan of all existing tags when merging tags from multiple value.Since the value tags are de-duplicated by the reference address when merging, the size of the value tags structure depends on the number of unique value tags that have been merged.
Use this behavior if you have structured data for tags and adding new tags with
add_value_tagis done often, since there is no additional serialization cost (unlike SVTAGS_UNIQUE_HASH), but there is additional merging cost.
Implementation
Value tags are implemented using the Value Magic feature that is being added to core Perl as part of Magic V2. By using Value Magic's infect callback, all of the value tags from all of the source variables are merged into the destination variable, as defined by the chosen behavior.
See ScalarValueMagicFunctions in "perlapi" for more details on scalar value magic in core Perl.
Basically,
Scalar value magic is added to a variable when a value tag is added
add_value_tag( $vt_type, \$var, 'foo' );Value tags are duplicated upon assignment from a tagged value
add_value_tag( $vt_type, \$foo, 'foo' ); $bar = $foo; # $bar now has the same 'foo' tag as $fooValue tags are merged when multiple source values are combined
$foo = 3; add_value_tag( $vt_type, \$foo, 'foo' ); $bar = 5; add_value_tag( $vt_type, \$bar, 'bar' ); $foo += $bar; # $foo now has both 'foo' and 'bar' tagsExisting value tags are removed when value is overwritten
$foo = 1; add_value_tag( $vt_type, \$foo, 'foo' ); $bar = 5; add_value_tag( $vt_type, \$bar, 'bar' ); $foo = $bar; # $foo now has only the 'bar' tagValue tags are removed when the value is set to
undef$foo = 1; add_value_tag( $vt_type, \$foo, 'foo' ); undef $foo; # $foo now has no tagsValue tags on source string are preserved through regexps
$foo = 'this is'; add_value_tag( $vt_type, \$foo, 'foo' ); ($bar) = $foo =~ m/is/; # $bar now has the 'foo' tagHash keys cannot contain value tags
$foo = 'foo'; add_value_tag( $vt_type, \$foo, 'foo' ); %bar = ( $foo => 8 ); for my $key ( keys %bar ) { # $key has no value tags }
FUNCTIONS
value_tags_enabled
if ( value_tags_enabled() ) {
say "Value tags are enabled!";
}
This constant is automatically exported into your namespace. It is true if the module is able to manage value tags, and false if not.
FIXME: Module needs to throw exception if value tags are not available and loaded, rather than requiring client to check whether they are enabled.
add_value_tag
add_value_tag( $vt_type, \$var, $tag );
add_value_tag adds the given tag to the value tags for the specified value-tags type.
$vt_type must be the value returned from a "register_value_tags_type" call.
The variable must always be passed as a reference, since add_value_tag needs to modify the SV* directly.
The $tag must be compatible with the registered behavior.
# using SVTAGS_UNIQUE_HASH or SVTAGS_HASH_COUNT
my $var;
add_value_tag( $vt_type, \$var, 'my tag' ); # tag must be string
my $tags = get_value_tags( $vt_type, \$var );
# returns { 'my tag' => true }
# using SVTAGS_UNIQUE_REF_ARRAY
my $var;
add_value_tag( $vt_type, \$var, [ 123 ] ); # tag must be ref
my $tags = get_value_tags( $vt_type, \$var );
# returns [ [ 123 ] ]
# using SVTAGS_APPEND_ARRAY
my $var;
add_value_tag( $vt_type, \$var, 'my tag' ); # tag may be ref or string
my $tags = get_value_tags( $vt_type, \$var );
# returns [ 'my tag' ]
remove_value_tag
remove_value_tag( $vt_type, \$var, $tag );
remove_value_tag removes the given tag to the value tags for the specified value-tags type.
$vt_type must be the value returned from a "register_value_tags_type" call.
The variable must always be passed as a reference, since add_value_tag needs to modify the SV* directly.
For tag types such as SVTAGS_APPEND_ARRAY, remove_value_tag removes all instances of the given tag from the array.
The $tag must be compatible with the registered behavior.
# using SVTAGS_UNIQUE_HASH or SVTAGS_HASH_COUNT
my $var;
remove_value_tag( $vt_type, \$var, 'my tag' ); # tag must be string
my $tags = get_value_tags( $vt_type, \$var );
# returns { 'my tag' => true }
# using SVTAGS_UNIQUE_REF_ARRAY
my $var;
remove_value_tag( $vt_type, \$var, [ 123 ] ); # tag must be ref
my $tags = get_value_tags( $vt_type, \$var );
# returns [ [ 123 ] ]
# using SVTAGS_APPEND_ARRAY
my $var;
remove_value_tag( $vt_type, \$var, 'my tag' ); # tag may be ref or string
my $tags = get_value_tags( $vt_type, \$var );
# returns [ 'my tag' ]
get_value_tags
$tags = get_value_tags( $vt_type, \$var );
The get_value_tags function returns the value tags of the given $vt_type that are currently attached to the variable's data.
The variable must always be passed as a reference, since add_value_tag needs to inspect the SV* directly.
The returned value tags structure depends on the registered behavior. Array behaviors return an array reference, while hash behaviors return a hash reference.
clear_value_tags
clear_value_tags( $vt_type, \$var );
The clear_value_tags function removes all value tags of the given $vt_type from the variable's data.
The variable must always be passed as a reference, since add_value_tag needs to modify the SV* directly.
DEBUGGING
FIXME - Debugging is not yet implemented in Scalar::ValueTags.
Devel::MAT::Dumper
If Devel::MAT::Dumper is installed, then Scalar::ValueTags will add any value tags to the dumped data.
See HAVE_DMD_HELPER in the XS code.
DEBUG_TRACE_ANNOTATIONS
If Scalar::ValueTags is configured with the --with-trace option, then additional Perl magic is added to each of the value tags indicating the source code origin of that annotation.
To enable this, use perl Build.PL --with-trace.
LICENSE
This module is released under the same terms as Perl itself.
AUTHORS
Noel Maddy <zhtwnpanta@gmail.com>