File Coverage

blib/lib/Language/Befunge/Storage/Generic/Vec.pm
Criterion Covered Total %
statement 58 58 100.0
branch 10 10 100.0
condition n/a
subroutine 16 16 100.0
pod 2 2 100.0
total 86 86 100.0


line stmt bran cond sub pod time code
1             #
2             # This file is part of Language::Befunge.
3             # Copyright (c) 2001-2009 Jerome Quelin, all rights reserved.
4             #
5             # This program is free software; you can redistribute it and/or modify
6             # it under the same terms as Perl itself.
7             #
8             #
9              
10             package Language::Befunge::Storage::Generic::Vec;
11             require 5.010;
12 6     6   50467 use strict;
  6         13  
  6         296  
13 6     6   110 use warnings;
  6         12  
  6         212  
14 6     6   30 no warnings 'portable'; # "Bit vector size > 32 non-portable" warnings on x64
  6         104  
  6         238  
15 6     6   35 use Carp;
  6         19  
  6         533  
16 6     6   691 use Language::Befunge::Vector;
  6         19  
  6         83  
17 6     6   785 use Language::Befunge::IP;
  6         12  
  6         177  
18 6     6   168 use base qw{ Language::Befunge::Storage };
  6         12  
  6         1332  
19 6     6   37 use Config;
  6         12  
  6         823  
20              
21             my $cell_size_in_bytes = $Config{ivsize};
22             my $cell_size_in_bits = $cell_size_in_bytes * 8;
23             # -- CONSTRUCTOR
24              
25              
26             # try to load speed-up LBSGVXS
27 6     6   5933 eval 'use Language::Befunge::Storage::Generic::Vec::XS';
  6         450956  
  6         114  
28             if ( defined $Language::Befunge::Storage::Generic::Vec::XS::VERSION ) {
29             my $xsversion = $Language::Befunge::Vector::XS::VERSION;
30             my @subs = qw[
31             get_value _get_value set_value _set_value _offset __offset _is_xs expand _expand
32             ];
33             foreach my $sub ( @subs ) {
34 6     6   35 no strict 'refs';
  6         1471  
  6         198  
35 6     6   143 no warnings 'redefine';
  6         11  
  6         7622  
36             my $lbsgvxs_sub = "Language::Befunge::Storage::Generic::Vec::XS::$sub";
37             *$sub = \&$lbsgvxs_sub;
38             }
39             }
40              
41              
42             #
43             # new( dimensions )
44             #
45             # Creates a new Lahey Space.
46             #
47             sub new {
48 16     16 1 13047 my $package = shift;
49 16         39 my $dimensions = shift;
50 16         66 my %args = @_;
51 16         62 my $usage = "Usage: $package->new(\$dimensions, Wrapping => \$wrapping)";
52 16 100       91 croak $usage unless defined $dimensions;
53 15 100       65 croak $usage unless $dimensions > 0;
54 14 100       67 croak $usage unless exists $args{Wrapping};
55 13         65 my $self = {
56             nd => $dimensions,
57             wrapping => $args{Wrapping},
58             };
59 13         58 bless $self, $package;
60 13         52 $self->clear();
61 13         260 return $self;
62             }
63              
64              
65             # -- PUBLIC METHODS
66              
67             #
68             # clear( )
69             #
70             # Clear the torus.
71             #
72             sub clear {
73 20     20 1 5008 my $self = shift;
74 20         246 $$self{min} = Language::Befunge::Vector->new_zeroes($$self{nd});
75 20         137 $$self{max} = Language::Befunge::Vector->new_zeroes($$self{nd});
76 20         333 $$self{torus} = chr(0) x $cell_size_in_bytes;
77 20         371 $self->set_value($$self{min}, 32);
78             }
79              
80              
81             #
82             # expand( v )
83             #
84             # Expand the torus to include the provided point.
85             #
86             sub expand {
87             my ($self, $point) = @_;
88             my ($old_min, $old_max) = ($$self{min}, $$self{max});
89             # if we have nothing to do, skip out early.
90             return if $point->bounds_check($$self{min}, $$self{max});
91              
92             $point = $point->copy();
93             my $nd = $$self{nd};
94              
95             my ($new_min, $new_max) = ($old_min->copy, $old_max->copy);
96             foreach my $d (0..$nd-1) {
97             $new_min->set_component($d, $point->get_component($d))
98             if $new_min->get_component($d) > $point->get_component($d);
99             $new_max->set_component($d, $point->get_component($d))
100             if $new_max->get_component($d) < $point->get_component($d);
101             }
102             my $old_size = $old_max - $old_min;
103             my $new_size = $new_max - $new_min;
104              
105             # figure out the new storage size
106             my $storage_size = $self->_offset($new_max, $new_min, $new_max) + 1;
107              
108             # figure out what a space looks like on this architecture.
109             # Note: vec() is always big-endian, but the XS module is host-endian.
110             # So we have to use an indirect approach.
111             my $old_value = $self->get_value($self->min);
112             $self->set_value($self->min, 32);
113             my $new_value = vec($$self{torus}, 0, $cell_size_in_bits);
114             $self->set_value($self->min, $old_value);
115             # allocate new storage
116             my $new_torus = " " x $cell_size_in_bytes;
117             vec($new_torus, 0, $cell_size_in_bits) = $new_value;
118             $new_torus x= $storage_size;
119             for(my $v = $new_min->copy; defined($v); $v = $v->rasterize($new_min, $new_max)) {
120             if($v->bounds_check($old_min, $old_max)) {
121             my $length = $old_max->get_component(0) - $v->get_component(0);
122             my $old_offset = $self->_offset($v);
123             my $new_offset = $self->_offset($v, $new_min, $new_max);
124             vec( $new_torus , $new_offset, $cell_size_in_bits)
125             = vec($$self{torus}, $old_offset, $cell_size_in_bits);
126             }
127             }
128             $$self{min} = $new_min;
129             $$self{max} = $new_max;
130             $$self{torus} = $new_torus;
131             }
132              
133              
134             #
135             # my $val = get_value( vector )
136             #
137             # Return the number stored in the torus at the specified location. If
138             # the value hasn't yet been set, it defaults to the ordinal value of a
139             # space (ie, #32).
140             #
141             # B As in Funge, code and data share the same playfield, the
142             # number returned can be either an instruction B a data (or even
143             # both... Eh, that's Funge! :o) ).
144             #
145             sub get_value {
146             my ($self, $v) = @_;
147             my $val = 32;
148              
149             if ($v->bounds_check($$self{min}, $$self{max})) {
150             my $off = $self->_offset($v);
151             $val = vec($$self{torus}, $off, $cell_size_in_bits);
152             }
153             return $self->_u32_to_s32($val);
154             }
155              
156              
157             #
158             # set_value( vector, value )
159             #
160             # Write the supplied value in the torus at the specified location.
161             #
162             # B As in Funge, code and data share the same playfield, the
163             # number stored can be either an instruction B a data (or even
164             # both... Eh, that's Funge! :o) ).
165             #
166             sub set_value {
167             my ($self, $v, $val) = @_;
168              
169             # Ensure we can set the value.
170             $self->expand($v);
171             my $off = $self->_offset($v);
172             vec($$self{torus}, $off, $cell_size_in_bits) = $self->_s32_to_u32($val);
173             }
174              
175              
176             # -- PRIVATE METHODS
177              
178             #
179             # _offset(v [, min, max])
180             #
181             # Return the offset (within the torus bitstring) of the vector. If min and max
182             # are provided, return the offset within a hypothetical torus which has those
183             # dimensions.
184             #
185             sub _offset {
186             my ($self, $v, $min, $max) = @_;
187             my $nd = $$self{nd};
188             my $off_by_1 = Language::Befunge::Vector->new(map { 1 } (1..$nd));
189             $min = $$self{min} unless defined $min;
190             $max = $$self{max} unless defined $max;
191             my $tsize = $max + $off_by_1 - $min;
192             my $toff = $v - $min;
193             my $rv = 0;
194             my $levsize = 1;
195             foreach my $d (0..$nd-1) {
196             $rv += $toff->get_component($d) * $levsize;
197             $levsize *= $tsize->get_component($d);
198             }
199             return $rv;
200             }
201              
202              
203             sub _s32_to_u32 {
204 2     2   400 my ($self, $value) = @_;
205 2 100       8 $value = 0xffffffff + ($value+1)
206             if $value < 0;
207 2         11 return $value;
208             }
209              
210             sub _u32_to_s32 {
211 2     2   9 my ($self, $value) = @_;
212 2 100       8 $value = -2147483648 + ($value & 0x7fffffff)
213             if($value & 0x80000000);
214 2         9 return $value;
215             }
216              
217             sub _copy {
218 1     1   1350 my $self = shift;
219 1         41 my $new = {
220             nd => $$self{nd},
221             min => $$self{min}->copy,
222             max => $$self{max}->copy,
223             torus => $$self{torus},
224             wrapping => $$self{wrapping},
225             };
226 1         6 return bless($new, ref($self));
227             }
228              
229             sub _is_xs { 0 }
230              
231             1;
232             __END__