File Coverage

blib/lib/Mock/Data/Regex.pm
Criterion Covered Total %
statement 206 250 82.4
branch 138 198 69.7
condition 65 120 54.1
subroutine 38 47 80.8
pod 11 35 31.4
total 458 650 70.4


line stmt bran cond sub pod time code
1             package Mock::Data::Regex;
2 9     9   239748 use strict;
  9         26  
  9         320  
3 9     9   49 use warnings;
  9         19  
  9         275  
4 9     9   566 use Mock::Data::Charset;
  9         20  
  9         288  
5 9     9   48 use Mock::Data::Util qw( _parse_context _escape_str );
  9         16  
  9         42821  
6             require Carp;
7             require Scalar::Util;
8             require List::Util;
9             require Hash::Util;
10             require Mock::Data::Generator;
11             our @ISA= ( 'Mock::Data::Generator' );
12              
13             # ABSTRACT: Generator that uses a Regex as a template to generate strings
14             our $VERSION = '0.02'; # VERSION
15              
16              
17             sub new {
18 22     22 1 51231 my $class= shift;
19             my %self= @_ == 1 && (!ref $_[0] || ref $_[0] eq 'Regexp')? ( regex => $_[0] )
20 22 50 66     242 : @_ == 1? %{$_[0]}
  0 100       0  
21             : @_;
22              
23             # If called on an object, carry over some settings
24 22 50       66 if (ref $class) {
25 0         0 %self= ( %$class, %self );
26             # Make sure we didn't copy a regex without a matching regex_parse_tree, or vice versa
27 0 0 0     0 if ($self{regex} == $class->{regex} xor $self{regex_parse_tree} == $class->{regex_parse_tree}) {
28 0 0       0 delete $self{regex_parse_tree} if $self{regex_parse_tree} == $class->{regex_parse_tree};
29 0 0       0 delete $self{regex} if $self{regex} == $class->{regex};
30             }
31 0         0 $class= ref $class;
32             }
33              
34 22 50       77 defined $self{regex} or Carp::croak "Attribute 'regex' is required";
35 22 50       124 $self{regex}= qr/$self{regex}/ unless ref $self{regex} eq 'Regexp';
36             # Must be parsed eventually, so might as well do it now and see the errors right away
37 22   33     127 $self{regex_parse_tree} ||= $class->parse($self{regex});
38 22 100 100     78 $self{max_codepoint} //= 0x7F if $self{regex_parse_tree}->flags->{a};
39              
40 22 50 0     77 $self{prefix} //= Mock::Data::Util::coerce_generator($self{prefix}) if defined $self{prefix};
41 22 50 0     54 $self{suffix} //= Mock::Data::Util::coerce_generator($self{suffix}) if defined $self{suffix};
42              
43 22         105 return bless \%self, $class;
44             }
45              
46              
47 0     0 1 0 sub regex { $_[0]{regex} }
48              
49 205     205 1 486 sub regex_parse_tree { $_[0]{regex_parse_tree} }
50              
51              
52             sub min_codepoint {
53             $_[0]{min_codepoint}
54 203     203 1 500 }
55              
56 203     203 1 643 sub max_codepoint { $_[0]{max_codepoint} }
57              
58              
59 205 50   205 1 877 sub max_repetition { $_[0]{max_repetition} || '+8' }
60              
61              
62             sub prefix {
63 0 0   0 1 0 if (@_ > 1) {
64 0         0 $_[0]{prefix}= Mock::Data::Util::coerce_generator($_[1]);
65             }
66             $_[0]{prefix}
67 0         0 }
68              
69             sub suffix {
70 0 0   0 1 0 if (@_ > 1) {
71 0         0 $_[0]{suffix}= Mock::Data::Util::coerce_generator($_[1]);
72             }
73             $_[0]{suffix}
74 0         0 }
75              
76              
77             sub generate {
78 205     205 1 11407 my ($self, $mockdata)= (shift,shift);
79 205 100       564 my %opts= ref $_[0] eq 'HASH'? %{$_[0]} : ();
  23         99  
80 205   100     920 $opts{max_codepoint} //= $self->max_codepoint;
81 205   100     789 $opts{min_codepoint} //= $self->min_codepoint;
82 205   33     674 $opts{max_repetition} //= $self->max_repetition;
83 205         521 my $out= $self->_str_builder($mockdata, \%opts);
84             $self->regex_parse_tree->generate($out)
85             # is the string allowed to end here? Requirement of '' is generated by $ and \Z
86 205 50 66     432 && (!$out->next_req || (grep $_ eq '', @{ $out->next_req }))
      66        
87             or Carp::croak "Regex assertions could not be met (such as '^' or '\$'). Final attempt was: \""._escape_str($out->str)."\"";
88 205   66     683 my $prefix= $opts{prefix} // $self->{prefix};
89 205   66     617 my $suffix= $opts{suffix} // $self->{suffix};
90 205 100 66     783 return $out->str unless defined $prefix || defined $suffix;
91              
92 19         53 my $str= $out->str;
93             # A prefix can only be added if there was not a beginning-of-string assertion, or if
94             # it was a ^/m assertion (flagged as "LF")
95 19 50 66     77 if ($prefix && (!$out->start || $out->start eq 'LF')) {
      66        
96 19         67 my $p= Mock::Data::Util::coerce_generator($prefix)->generate($mockdata);
97 19 100       73 $p .= "\n" if $out->start;
98 19         59 $str= $p . $str;
99             }
100             # A suffix can only be added if there was not an end-of-string assertion, or if
101             # the next assertion allows "\n" and there is no assertion after that.
102 19 100 100     83 if ($suffix && (!$out->next_req || (grep $_ eq "\n", @{ $out->next_req }) && !$out->require->[1])) {
      66        
103 18 100       44 $str .= "\n" if $out->next_req;
104 18         55 $str .= Mock::Data::Util::coerce_generator($suffix)->generate($mockdata);
105             }
106 19         101 return $str;
107             }
108              
109              
110             sub parse {
111 40     40 1 22937 my ($self, $regex)= @_;
112 40         205 return $self->_parse_regex({}) for "$regex";
113             }
114              
115             sub get_charset {
116 0     0 1 0 my $self= shift;
117 0         0 my $p= $self->regex_parse_tree->pattern;
118 0 0 0     0 return Scalar::Util::blessed($p) && $p->isa('Mock::Data::Charset')? $p : undef;
119             }
120              
121             our %_regex_syntax_unsupported= (
122             '' => { map { $_ => 1 } qw( $ ) },
123             '\\' => { map { $_ => 1 } qw( B b A Z z G g K k ) },
124             );
125             our %_parse_regex_backslash= (
126             map +( $_ => $Mock::Data::Charset::_parse_charset_backslash{$_} ),
127             qw( a b c e f n N o r t x 0 1 2 3 4 5 6 7 8 9 )
128             );
129             sub _parse_regex {
130 91     91   152 my $self= shift;
131 91   50     194 my $flags= shift || {};
132 91         146 my $expr= [];
133 91         142 my @or;
134 91         124 while (1) {
135             # begin parenthetical sub-expression?
136 220 100       681 if (/\G \( (\?)? /gcx) {
137 51         96 my $sub_flags= $flags;
138 51 100       150 if (defined $1) {
139             # leading question mark means regex flags. This only supports the ^...: one:
140 40 50 0     219 if (/\G \^ ( \w* ) : /gcx) {
    0          
141 40         88 $sub_flags= {};
142 40         177 ++$sub_flags->{$_} for split '', $1;
143             } elsif ($] < 5.020 and /\G (\w*)-\w* : /gcx) {
144 0         0 $sub_flags= {};
145 0         0 ++$sub_flags->{$_} for split '', $1;
146             } else {
147 0         0 Carp::croak("Unsupported regex feature '(?".substr($_,pos,1)."'");
148             }
149             }
150 51         116 my $pos= pos;
151 51         168 push @$expr, $self->_parse_regex($sub_flags);
152 51 50       185 /\G \) /gcx
153             or die "Missing end-parenthesee, started at '"._parse_context($pos)."'";
154             }
155             # end sub-expression or next alternation?
156 220 100 33     1090 if (/\G ( [|)] ) /gcx) {
    100          
    100          
    50          
    100          
157             # end of sub-expression, return.
158 56 100       156 if ($1 eq ')') {
159             # back it up so the caller knows why we exited
160 51         129 --pos;
161 51         123 last;
162             }
163             # else begin next piece of @or
164 5         15 push @or, $self->_node($expr, $flags);
165 5         11 $expr= [];
166             }
167             # character class?
168             elsif (/\G ( \[ | \\w | \\W | \\s | \\S | \\d | \\D | \\N | \\Z | \. | \^ | \$ ) /gcx) {
169 25 100       154 if ($1 eq '[') {
    100          
    100          
    100          
    50          
170             # parse function continues to operate on $_ at pos()
171 5         23 my $parse= Mock::Data::Charset::_parse_charset($flags);
172 5         21 push @$expr, $self->_charset_node($parse, $flags);
173             }
174             elsif (ord $1 == ord '\\') {
175 5 100       12 if ($1 eq "\\Z") {
176 1         6 push @$expr, $self->_assertion_node(end => 1, flags => $flags);
177             }
178             else {
179 4         14 push @$expr, $self->_charset_node(notation => $1, $flags);
180             }
181             }
182             elsif ($1 eq '.') {
183 2 50       11 push @$expr, $self->_charset_node(classes => [ $flags->{s}? 'Any' : '\\N' ], $flags);
184             }
185             elsif ($1 eq '$') {
186 7 100       29 push @$expr, $self->_assertion_node(end => ($flags->{m}? 'LF' : 'FinalLF'), flags => $flags);
187             }
188             elsif ($1 eq '^') {
189 6 100       37 push @$expr, $self->_assertion_node(start => ($flags->{m}? 'LF' : 1 ), flags => $flags);
190             }
191             }
192             # repetition?
193             elsif (/\G ( \? | \* \?? | \+ \?? | \{ ([0-9]+)? (,)? ([0-9]+)? \} ) /gcx) {
194 24         49 my @rep;
195 24 100       126 if ($1 eq '?') {
    100          
    100          
196 1         3 @rep= (0,1);
197             }
198             elsif (ord $1 == ord '*') {
199 5         14 @rep= (0);
200             }
201             elsif (ord $1 == ord '+') {
202 13         36 @rep= (1);
203             }
204             else {
205 5 100 100     31 @rep= $3? ($2||0,$4) : ($2||0,$2);
      50        
206             }
207             # What came before this?
208 24 50       97 if (!@$expr) {
    100          
209 0         0 die "Found quantifier '$1' before anything to quantify at "._parse_context;
210             }
211             elsif (!ref $expr->[-1]) {
212             # If the string is composed of more than one character, split the final one
213             # into its own node so that it can have a repetition applied to it.
214 6 100       50 if (length $expr->[-1] > 1) {
215 2         13 push @$expr, $self->_node([ substr($expr->[-1], -1) ], $flags);
216 2         6 substr($expr->[-2], -1)= '';
217             }
218             # else its one character, wrap it in a node
219             else {
220 4         16 $expr->[-1]= $self->_node([ $expr->[-1] ], $flags);
221             }
222             }
223 24         79 $expr->[-1]->repetition(\@rep)
224             }
225             elsif ($flags->{x} && /\G ( \s | [#].* ) /gcx) {
226             # ignore whitespace and comments under /x mode
227             }
228             elsif (/\G (\\)? (.) /gcxs) {
229             # Tell users about unsupported features
230 75 50 100     401 die "Unsupported notation: '$1$2'" if $_regex_syntax_unsupported{$1||''}{$2};
231 75         113 my $ch;
232 75 100 100     226 if ($1 && defined (my $equiv= $_parse_regex_backslash{$2})) {
233 5 100       30 $ch= chr(ref $equiv? $equiv->() : $equiv);
234             } else {
235 70         155 $ch= $2;
236             }
237 75 100 66     342 if ($flags->{i} && (uc $ch ne lc $ch)) {
    100 100        
238 3         48 push @$expr, $self->_charset_node(chars => [uc $ch, lc $ch], $flags);
239             }
240             elsif (@$expr && !ref $expr->[-1]) {
241 22         45 $expr->[-1] .= $ch;
242             }
243             else {
244 50         138 push @$expr, $ch;
245             }
246             }
247             else {
248 40         68 last; # end of string
249             }
250             }
251 91 50 100     583 return @or? do { push @or, $self->_node($expr, $flags) if @$expr; $self->_or_node(\@or, $flags) }
  3 100       12  
  3 100       12  
252             : (@$expr > 1 || !ref $expr->[0])? $self->_node($expr, $flags)
253             : $expr->[0];
254             }
255              
256             #----------------------------------
257             # Factory Functions for Parse Nodes
258              
259             sub _node {
260 49     49   134 my ($self, $pattern, $flags)= @_;
261 49         198 Mock::Data::Regex::ParseNode->new({ pattern => $pattern, flags => $flags });
262             }
263             sub _or_node {
264 3     3   9 my ($self, $or_list, $flags)= @_;
265 3         21 Mock::Data::Regex::ParseNode::Or->new({ pattern => $or_list, flags => $flags });
266             }
267             sub _charset_node {
268 14     14   27 my $self= shift;
269 14         24 my $flags= pop;
270 14 100       126 Mock::Data::Regex::ParseNode::Charset->new({
271             pattern => @_ > 1? { @_ } : shift,
272             flags => $flags
273             });
274             }
275             sub _assertion_node {
276 14     14   29 my $self= shift;
277 14         85 Mock::Data::Regex::ParseNode::Assertion->new({ @_ });
278             }
279             sub _str_builder {
280 205     205   393 my ($self, $mockdata, $opts)= @_;
281 205         889 Mock::Data::Regex::StrBuilder->new({
282             mockdata => $mockdata,
283             generator => $self,
284             opts => $opts,
285             });
286             }
287              
288             sub _fake_inc {
289 45     45   263 (my $pkg= caller) =~ s,::,/,g;
290 45         178 $INC{$pkg.'.pm'}= $INC{'Mock/Data/Generator/Regex.pm'};
291             }
292              
293             # ------------------------------ Regex Parse Node -------------------------------------
294             # The regular parse nodes hold a "pattern" which is an arrayref of literal strings
295             # or nested parse nodes. It supports a "repetition" flag to handle min/max repetitions
296             # of the node as a whole.
297             # Other subclasses are used to handle OR-lists, charsets, and zero-width assertions.
298              
299             package # Do not index
300             Mock::Data::Regex::ParseNode;
301             Mock::Data::Regex::_fake_inc();
302              
303 66     66 0 204 sub new { bless $_[1], $_[0] }
304              
305 22     22 0 80 sub flags { $_[0]{flags} }
306             sub repetition {
307 448 100   448 0 3223 if (@_ > 1) {
308             # If a quantifier is being applied to a thing that already had a quantifier
309             # (such as /(X*){2}/ )
310             # multiply them
311 24         38 my $val= $_[1];
312 24 50       54 if (my $rep= $_[0]{repetition}) {
313             $rep->[$_]= (defined $rep->[$_] && defined $val->[$_]? $rep->[$_] * $val->[$_] : undef)
314 0 0 0     0 for 0, 1;
315             }
316             else {
317 24         44 $_[0]{repetition}= $_[1];
318             }
319             }
320             return $_[0]{repetition}
321 448         923 }
322             sub min_repetition {
323 0 0   0 0 0 $_[0]{repetition}? $_[0]{repetition}[0] : 1
324             }
325             sub max_repetition {
326 0 0   0 0 0 $_[0]{repetition}? $_[0]{repetition}[1] : 1
327             }
328 218     218 0 11421 sub pattern { $_[0]{pattern} }
329             sub generate {
330 341     341 0 701 my ($self, $out)= @_;
331 341 100       650 if (my $rep= $self->repetition) {
332 82         182 my ($min, $n)= ($rep->[0], $out->_random_rep_count($rep));
333 82         181 for (1 .. $n) {
334 325 100       655 my $origin= $_ > $min? $out->mark : undef;
335             # Plain nodes expect the pattern to be an arrayref where each item is a parse node or a literal
336 325         426 my $success= 1;
337 325         400 for (@{ $self->{pattern} }) {
  325         517  
338 364 100 100     880 $success &&= ref $_? $_->generate($out) : $out->append($_);
339             }
340 325 100       709 next if $success;
341             # This repetition failed, but did we meet the requirement already?
342 19 50       37 if ($origin) {
343 19         45 $out->reset($origin);
344 19         58 return 1;
345             }
346 0         0 return 0;
347             }
348             }
349             else {
350             # Plain nodes expect the pattern to be an arrayref where each item is a parse node or a literal
351 259         400 for (@{ $self->{pattern} }) {
  259         545  
352 433 100       1072 return 0 unless ref $_? $_->generate($out) : $out->append($_);
    100          
353             }
354             }
355 299         893 return 1;
356             }
357              
358             # --------------------------------- Regex "OR" Parse Node ----------------------------
359             # This parse holds a list of options in ->pattern. It chooses one of the options at
360             # random, but then can backtrack if inner parse nodes were not able to match.
361              
362             package # Do not index
363             Mock::Data::Regex::ParseNode::Or;
364             Mock::Data::Regex::_fake_inc();
365             our @ISA= ('Mock::Data::Regex::ParseNode');
366              
367             sub generate {
368 30     30 0 68 my ($self, $out)= @_;
369 30         60 my ($min, $n)= (1,1);
370 30 50       85 if (my $rep= $self->{repetition}) {
371 30         52 $min= $rep->[0];
372 30         73 $n= $out->_random_rep_count($rep);
373             }
374 30         72 rep: for (1 .. $n) {
375             # OR nodes expect the pattern to be an arrayref where each item is an option
376             # for what could be appended. Need to reset the output after each attempt.
377 117         240 my $origin= $out->mark;
378             # Pick one at random. It will almost always work on the first try, unless the user
379             # has anchor constraints in the pattern.
380 117         240 my $or= $self->pattern;
381 117         247 my $pick= $or->[ rand scalar @$or ];
382 117 50       336 next rep if ref $pick? $pick->generate($out) : $out->append($pick);
    100          
383             # if it fails, try all the others in random order
384 23         55 for (List::Util::shuffle(grep { $_ != $pick } @$or)) {
  46         155  
385             # reset output
386 23         73 $out->reset($origin);
387             # append something new
388 23 50       70 next rep if ref $_? $_->generate($out) : $out->append($_);
    50          
389             }
390             # None of the options succeeded. Did we get enough reps already?
391 0 0       0 if ($_ > $min) {
392 0         0 $out->reset($origin);
393 0         0 return 1;
394             }
395 0         0 return 0;
396             }
397 30         84 return 1;
398             }
399              
400             # -------------------------------- Regex Charset Parse Node ---------------------------
401             # This node's ->pattern is an instance of Charset. It returns one character
402             # from the set, but also has an optimized handling of the ->repetition flag that generates
403             # multiple characters at once.
404              
405             package # Do not index
406             Mock::Data::Regex::ParseNode::Charset;
407             Mock::Data::Regex::_fake_inc();
408             our @ISA= ('Mock::Data::Regex::ParseNode');
409              
410             sub new {
411 14     14 0 31 my ($class, $self)= @_;
412 14 50       53 if (ref $self->{pattern} eq 'HASH') {
413 14 100       40 $self->{pattern}{max_codepoint}= 0x7F if $self->{flags}{a};
414 14         50 $self->{pattern}= Mock::Data::Util::charset($self->{pattern});
415             }
416 14         58 bless $self, $class;
417             }
418              
419             sub generate {
420 76     76 0 556 my ($self, $out)= @_;
421             # Check whether output has a restriction in effect:
422 76 50       143 if (my $req= $out->next_req) {
423             # pick the first requirement which can be matched by this charset
424 0         0 for (@$req) {
425 0 0       0 if (!ref) {
426             # At \Z, can still match if rep count is 0
427 0 0 0     0 return 1 if length == 0 && $self->min_repetition == 0;
428 0 0 0     0 return $out->append($_) if
      0        
      0        
429             length == 1 && $self->pattern->has_member($_)
430             or length > 1 && !(grep !$self->pattern->has_member($_), split //);
431             }
432             }
433 0         0 return 0;
434             }
435 76         145 my $n= $out->_random_rep_count($self->repetition);
436 76         156 return $out->append($self->pattern->generate($out->mockdata, $out->opts, $n));
437             }
438              
439             # ----------------------------- Regex Assertion Parse Node -------------------------------
440             # This node doesn't have a ->pattern, and instead holds constraints about what characters
441             # must occur around the current position. Right now it only handles '^' and '$' and '\Z'
442              
443             package # Do not index
444             Mock::Data::Regex::ParseNode::Assertion;
445             Mock::Data::Regex::_fake_inc();
446             our @ISA= ('Mock::Data::Regex::ParseNode');
447              
448 0     0 0 0 sub start { $_[0]{start} }
449 0     0 0 0 sub end { $_[0]{end} }
450             sub generate {
451 95     95 0 183 my ($self, $out)= @_;
452 95 100       218 if ($self->{start}) {
453             # Previous character must either be start of string or a newline
454             length $out->str == 0
455 56 100 66     111 or ($self->{start} eq 'LF' && substr($out->str,-1) eq "\n")
      100        
456             or return 0;
457             # Set flag on entire output if this is the first assertion
458 33 100 66     76 $out->start($self->{start}) if length $out->str == 0 && !$out->start;
459             }
460 72 100       153 if ($self->{end}) {
461             # Next character must be a newline, or end of the output
462             # end=1 results from \Z and does not allow the newline
463 39 50       167 $out->require(['',"\n"]) unless $self->{end} eq 1;
464             # If end=LF, the end of string is no longer mandatory once "\n" has been matched.
465 39 100       95 $out->require(['']) unless $self->{end} eq 'LF';
466             }
467 72         175 return 1;
468             }
469              
470             # ------------------------ String Builder -----------------------------------
471             # This class constructs an output string in ->{str}, and also performs checks
472             # needed by the assertions like ^ and $. It also has the ability to mark a
473             # position and then revert to that position, without copying the entire string
474             # each time.
475              
476             package # Do not index
477             Mock::Data::Regex::StrBuilder;
478             Mock::Data::Regex::_fake_inc();
479              
480             sub new {
481 205     205 0 396 my ($class, $self)= @_;
482 205   50     782 $self->{str} //= '';
483 205         525 bless $self, $class;
484             }
485              
486 76     76 0 145 sub mockdata { $_[0]{mockdata} } # Mock::Data instance
487 0     0 0 0 sub generator { $_[0]{generator} }
488 198     198 0 521 sub opts { $_[0]{opts} }
489 453 100   453 0 890 sub start { $_[0]{start}= $_[1] if @_ > 1; $_[0]{start} }
  453         1016  
490 331     331 0 1533 sub str { $_[0]{str} } # string being built
491             sub _random_rep_count {
492 188     188   333 my ($self, $rep)= @_;
493 188 100       421 return 1 unless defined $rep;
494 148 100       392 return $rep->[0] + int rand($rep->[1] - $rep->[0] + 1)
495             if defined $rep->[1];
496 122   50     347 my $range= $self->opts->{max_repetition} // '+8';
497 122 50       643 return $rep->[0] + int rand($range+1)
498             if ord $range == ord '+';
499 0         0 $range -= $rep->[0];
500 0 0       0 return $range > 0? $rep->[0] + int rand($range+1) : $rep->[0];
501             }
502              
503             sub require {
504 61 100   61 0 114 push @{ $_[0]{require} }, $_[1] if @_ > 1;
  58         139  
505 61         102 return $_[0]{require};
506             }
507             sub next_req {
508 1017   66 1017 0 2932 return $_[0]{require} && $_[0]{require}[0];
509             }
510             sub append {
511 657     657 0 1186 my ($self, $content)= @_;
512 657 100       1146 if (my $req= $self->next_req) {
513             # the provided output must be coerced to one of these options, if possible
514             # TODO: need new ideas for this code. Or just give up on the plan of supporting
515             # lookaround assertions and focus on a simple implemention of "\n" checks for ^/$
516 19         29 for (@$req) {
517 38 50       59 if (!ref) { # next text must match a literal string. '' means end-of-string
518 38 50 66     111 if (length && $content eq $_) {
519 0         0 $self->{str} .= $content;
520 0         0 shift @{ $self->require }; # requirement complete
  0         0  
521 0         0 return 1;
522             }
523             }
524             else {
525             # TODO: support for "lookaround" assertions, will require regex match
526 0         0 die "Unimplemented: zero-width lookaround assertions";
527             }
528             }
529 19         55 return 0; # no match found for the restriction in effect
530             }
531 638         1110 $self->{str} .= $content;
532 638         1524 return 1;
533             }
534             sub mark {
535 376     376 0 521 my $self= shift;
536 376         620 my $len= $self->{lastmark}= length $self->{str};
537 376         497 my $req= $self->{require};
538 376 100       843 return [ \$self->{str}, $len, $req? [ @$req ] : undef, $self->start ];
539             }
540             sub reset {
541 42     42 0 89 my ($self, $origin)= @_;
542             # If the string is a different instance than before, go back to that instance
543 0         0 Hash::Util::hv_store(%$self, 'str', ${$origin->[0]})
544 42 50       143 unless \$self->{str} == $origin->[0];
545             # Reset the string to the original length
546 42         109 substr($self->{str}, $origin->[1])= '';
547 42         103 $self->{require}= $origin->[2];
548 42         82 $self->{start}= $origin->[3];
549             }
550              
551             1;
552              
553             __END__