From 6edfeb6fcbfe718fb13288060307f919e10d607a Mon Sep 17 00:00:00 2001 From: Gianni Ceccarelli Date: Mon, 14 Nov 2011 15:52:01 +0000 Subject: more docs --- lib/Data/MultiValued/RangeContainer.pm | 83 +++++++++++++++++++++++++-- lib/Data/MultiValued/Ranges.pm | 53 +++++++++++------ lib/Data/MultiValued/TagContainerForRanges.pm | 9 ++- lib/Data/MultiValued/Tags.pm | 44 +++++++++----- lib/Data/MultiValued/TagsAndRanges.pm | 50 +++++++++++----- 5 files changed, 190 insertions(+), 49 deletions(-) diff --git a/lib/Data/MultiValued/RangeContainer.pm b/lib/Data/MultiValued/RangeContainer.pm index e9b1b62..0bfe8cd 100644 --- a/lib/Data/MultiValued/RangeContainer.pm +++ b/lib/Data/MultiValued/RangeContainer.pm @@ -5,6 +5,27 @@ use MooseX::Types::Moose qw(Num Str Any Undef ArrayRef); use MooseX::Types::Structured qw(Dict); use Data::MultiValued::Exceptions; +# ABSTRACT: container for ranged values + +=head1 DESCRIPTION + +Please don't use this module directly, use L. + +This module implements the storage for ranged data. It's similar to +L, but simpler (and slower). + +A range is defined by a pair of numbers, C and C, and it +contains C<< Num $x : $min <= $x < $max >>. C is treated as +"inf" (negative infinity if used as C or C, positive +infinity if used as C). + +The internal representation of a range is a hash with three keys, +C C C. + +=head1 METHODS + +=cut + has _storage => ( is => 'rw', isa => ArrayRef[ @@ -18,6 +39,16 @@ has _storage => ( default => sub { [ ] }, ); +=head2 C + + my $value = $obj->get({ at => $point }); + +Retrieves the range that includes the given point. Throws a +L exception if no range +includes the point. + +=cut + sub get { my ($self,$args) = @_; @@ -45,6 +76,8 @@ sub _cmp { return $a <=> $b; } +# a binary search would be a good idea. + sub _get_slot_at { my ($self,$at) = @_; @@ -56,6 +89,10 @@ sub _get_slot_at { return; } +# this is quite probably uselessly slow: we don't really need all of +# @before and @after, we just need to know if they're not empty; also, +# a binary search would be a good idea. + sub _partition_slots { my ($self,$from,$to) = @_; @@ -80,7 +117,18 @@ sub _partition_slots { return \@before,\@overlap,\@after; } -sub set_or_create { +=head2 C + + $obj->get_or_create({ from => $min, to => $max }); + +Retrieves the range that has the given extremes. If no such range +exists, creates a new range, splicing any existing overlapping range, +and returns it. Throws L if +C<< $min > $max >>. + +=cut + +sub get_or_create { my ($self,$args) = @_; my $from = $args->{from}; @@ -103,6 +151,19 @@ sub set_or_create { return $range; } +=head2 C + + $obj->clear({ from => $min, to => $max }); + +Removes the range that has the given extremes. If no such range +exists, splices any existing overlapping range so that C<< +$obj->get({at => $point }) >> for any C<< $min <= $point < $max >> +will die. + +Throws L if C<< $min > $max >>. + +=cut + sub clear { my ($self,$args) = @_; @@ -133,10 +194,16 @@ sub _clear_slot { $self->_splice_slot($from,$to); } +# Most of the splicing mechanics is here. Given a range and something +# to put in it, do "the right thing" + sub _splice_slot { my ($self,$from,$to,$new) = @_; - if (!@{$self->_storage}) { # empty + # if !$new, it's like C without a replacement list: we + # just delete the range + + if (!@{$self->_storage}) { # empty, just store push @{$self->_storage},$new if $new; return $new; } @@ -144,27 +211,33 @@ sub _splice_slot { my ($before,$overlap,$after) = $self->_partition_slots($from,$to); if (!@$before && !@$overlap) { + # nothing before, nothing overlapping: put $new at the beginning unshift @{$self->_storage},$new if $new; return $new; } if (!@$after && !@$overlap) { + # nothing after, nothing overlapping: put $new at the end push @{$self->_storage},$new if $new; return $new; } - # by costruction, the first and the last may have to be split, all - # others must be removed + # ok, we have to insert in the middle of things, and maybe we have + # to trim existing ranges + my $first_to_replace; my $how_many = @$overlap; my @replacement = $new ? ($new) : (); if ($how_many > 0) { # we have to splice + # by costruction, the first and the last may have to be split, all + # others must be removed $first_to_replace = $overlap->[0]; my $last_to_replace = $overlap->[-1]; my $first = $self->_storage->[$first_to_replace]; my $last = $self->_storage->[$last_to_replace]; + # does the first overlapping range need trimming? if (_cmp($first->{from},$from,0,0)<0 && _cmp($first->{to},$from,1,0)>=0) { unshift @replacement, { @@ -173,6 +246,7 @@ sub _splice_slot { value => $first->{value}, } } + # does the last overlapping range need trimming? if (_cmp($last->{from},$to,0,1)<=0 && _cmp($last->{to},$to,1,1)>0) { push @replacement, { @@ -183,6 +257,7 @@ sub _splice_slot { } } else { + # no overlaps, just insert between @before and @after $first_to_replace = $before->[-1]+1; } diff --git a/lib/Data/MultiValued/Ranges.pm b/lib/Data/MultiValued/Ranges.pm index 21898af..7193df6 100644 --- a/lib/Data/MultiValued/Ranges.pm +++ b/lib/Data/MultiValued/Ranges.pm @@ -36,24 +36,12 @@ sub _build__storage { Data::MultiValued::RangeContainer->new(); } -sub _rebless_storage { - my ($self) = @_; - - bless $self->{_storage},'Data::MultiValued::RangeContainer'; -} - -sub _as_hash { - my ($self) = @_; - - my %ret = %{$self->_storage}; - return {_storage=>\%ret}; -} - =head2 C $obj->set({ from => $min, to => $max, value => $the_value }); -Stores the given value for the given range. Does not throw exceptions. +Stores the given value for the given range. Throws +L if C<< $min > $max >>. The range is defined as C<< Num $x : $min <= $x < $max >>. A C<< from => undef >> means "from -Inf", and a C<< to => undef >> means "to @@ -90,7 +78,7 @@ sub set { value => { isa => Any, }, ); - $self->_storage->set_or_create(\%args) + $self->_storage->get_or_create(\%args) ->{value} = $args{value}; } @@ -125,7 +113,8 @@ sub get { $obj->clear({ from => $min, to => $max }); -Deletes all values for the given range. Does not throw exceptions. +Deletes all values for the given range. Throws +L if C<< $min > $max >>. A C<< from => undef >> means "from -Inf", and a C<< to => undef >> means "to +Inf". Not passing in C or C is equivalent to @@ -158,6 +147,38 @@ sub clear { $self->_storage->clear(\%args); } +=head1 Serialisation helpers + +These are used through +L. + +=head2 C<_rebless_storage> + +Blesses the storage into L. + +=cut + +sub _rebless_storage { + my ($self) = @_; + + bless $self->{_storage},'Data::MultiValued::RangeContainer'; +} + + +=head2 C<_as_hash> + +Returns the internal representation with no blessed hashes, with as +few copies as possible. + +=cut + +sub _as_hash { + my ($self) = @_; + + my %ret = %{$self->_storage}; + return {_storage=>\%ret}; +} + =head1 SEE ALSO L, L diff --git a/lib/Data/MultiValued/TagContainerForRanges.pm b/lib/Data/MultiValued/TagContainerForRanges.pm index 9cfc617..826df9d 100644 --- a/lib/Data/MultiValued/TagContainerForRanges.pm +++ b/lib/Data/MultiValued/TagContainerForRanges.pm @@ -46,7 +46,7 @@ L. =head2 C<_rebless_storage> -Blesses the "storage cells" +Blesses the "storage cells" into L. =cut @@ -58,6 +58,13 @@ sub _rebless_storage { return; } +=head2 C<_as_hash> + +Returns the internal representation with no blessed hashes, with as +few copies as possible. + +=cut + sub _as_hash { my ($self) = @_; my %st; diff --git a/lib/Data/MultiValued/Tags.pm b/lib/Data/MultiValued/Tags.pm index 083bbb8..9c52510 100644 --- a/lib/Data/MultiValued/Tags.pm +++ b/lib/Data/MultiValued/Tags.pm @@ -35,19 +35,6 @@ sub _build__storage { Data::MultiValued::TagContainer->new(); } -sub _rebless_storage { - my ($self) = @_; - - bless $self->{_storage},'Data::MultiValued::TagContainer'; -} - -sub _as_hash { - my ($self) = @_; - - my %ret = %{$self->_storage}; - return {_storage=>\%ret}; -} - =head2 C $obj->set({ tag => $the_tag, value => $the_value }); @@ -121,6 +108,37 @@ sub clear { $self->_storage->clear(\%args); } +=head1 Serialisation helpers + +These are used through +L. + +=head2 C<_rebless_storage> + +Blesses the storage into L. + +=cut + +sub _rebless_storage { + my ($self) = @_; + + bless $self->{_storage},'Data::MultiValued::TagContainer'; +} + +=head2 C<_as_hash> + +Returns the internal representation with no blessed hashes, with as +few copies as possible. + +=cut + +sub _as_hash { + my ($self) = @_; + + my %ret = %{$self->_storage}; + return {_storage=>\%ret}; +} + =head1 SEE ALSO L, L diff --git a/lib/Data/MultiValued/TagsAndRanges.pm b/lib/Data/MultiValued/TagsAndRanges.pm index 32e2acb..204f858 100644 --- a/lib/Data/MultiValued/TagsAndRanges.pm +++ b/lib/Data/MultiValued/TagsAndRanges.pm @@ -38,20 +38,6 @@ sub _build__storage { Data::MultiValued::TagContainerForRanges->new(); } -sub _rebless_storage { - my ($self) = @_; - - bless $self->{_storage},'Data::MultiValued::TagContainerForRanges'; - $self->_storage->_rebless_storage; -} - -sub _as_hash { - my ($self) = @_; - - my $ret = $self->_storage->_as_hash; - return {_storage=>$ret}; -} - =head2 C $obj->set({ tag => $the_tag, from => $min, to => $max, value => $the_value }); @@ -74,7 +60,7 @@ sub set { ); $self->_storage->get_or_create(\%args) - ->set_or_create(\%args) + ->get_or_create(\%args) ->{value} = $args{value}; } @@ -136,4 +122,38 @@ sub clear { } } +=head1 Serialisation helpers + +These are used through +L. + +=head2 C<_rebless_storage> + +Blesses the storage into L, +then calls C<_rebless_storage> on it. + +=cut + +sub _rebless_storage { + my ($self) = @_; + + bless $self->{_storage},'Data::MultiValued::TagContainerForRanges'; + $self->_storage->_rebless_storage; +} + +=head2 C<_as_hash> + +Returns the internal representation with no blessed hashes, with as +few copies as possible. Depends on +L. + +=cut + +sub _as_hash { + my ($self) = @_; + + my $ret = $self->_storage->_as_hash; + return {_storage=>$ret}; +} + 1; -- cgit v1.2.3