File Coverage

blib/lib/Language/Befunge/Storage/Generic/Vec.pm
Criterion Covered Total %
statement 111 113 98.2
branch 24 24 100.0
condition n/a
subroutine 22 22 100.0
pod 5 5 100.0
total 162 164 98.7


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