@@ -22,14 +22,20 @@ sub from_string {
2222 confess ' Argument must be a string: $domain'
2323 if !defined $domain || ref $domain ne ' ' ;
2424
25- return $class -> _new( { labels => [ split ( / [.]/x , $domain ) ] } );
25+ my $obj = Class::Accessor::new( $class , { labels => [ split ( / [.]/x , $domain ) ] } );
26+
27+ # We have the raw string, so we can precompute the string representation
28+ # easily and cheaply so it can be immediately returned by the string()
29+ # method instead of recomputing it from the labels list. The only thing we
30+ # need to do is to remove any trailing dot except if it’s the only
31+ # character.
32+ $obj -> {_string } = ( $domain =~ s / .\K [.] \z // rx );
33+
34+ return $obj ;
2635}
2736
2837sub new {
29- my $proto = shift ;
30- confess " must be called with a single argument"
31- if scalar ( @_ ) != 1;
32- my $input = shift ;
38+ my ( $class , $input ) = @_ ;
3339
3440 my $attrs = {};
3541 if ( !defined $input ) {
@@ -39,7 +45,7 @@ sub new {
3945 $attrs -> {labels } = \@{ $input -> labels };
4046 }
4147 elsif ( blessed $input && $input -> isa( ' Zonemaster::Engine::Zone' ) ) {
42- $attrs -> {labels } = [ split ( / [.] / x , $input -> name ) ] ;
48+ $attrs -> {labels } = \@{ $input -> name-> labels } ;
4349 }
4450 elsif ( ref $input eq ' ' ) {
4551 $attrs -> {labels } = [ split ( / [.]/x , $input ) ];
@@ -62,31 +68,20 @@ sub new {
6268 confess " Unrecognized argument: " . $what ;
6369 }
6470
65- # Type constraints
66- confess " Argument must be an ARRAYREF: labels"
67- if exists $attrs -> {labels }
68- && ref $attrs -> {labels } ne ' ARRAY' ;
69-
70- my $class = ref $proto || $proto ;
71- return $class -> _new( $attrs );
72- }
73-
74- sub _new {
75- my $class = shift ;
76- my $attrs = shift ;
77-
78- my $obj = Class::Accessor::new( $class , $attrs );
79-
80- return $obj ;
71+ return Class::Accessor::new( $class , $attrs );
8172}
8273
8374sub string {
8475 my $self = shift ;
8576
86- my $name = join ( ' .' , @{ $self -> labels } );
87- $name = ' .' if $name eq q{ } ;
77+ if ( not exists $self -> {_string } ) {
78+ my $string = join ( ' .' , @{ $self -> labels } );
79+ $string = ' .' if $string eq q{ } ;
80+
81+ $self -> {_string } = $string ;
82+ }
8883
89- return $name ;
84+ return $self -> { _string } ;
9085}
9186
9287sub fqdn {
@@ -96,12 +91,21 @@ sub fqdn {
9691}
9792
9893sub str_cmp {
99- my ( $self , $other ) = @_ ;
100- $other //= q{ } ; # Treat undefined value as root
94+ # For performance reasons, we do not unpack @_.
95+ # As a reminder, the calling convention is my ( $self, $other, $swap ) = @_.
10196
102- $other =~ s / (.+)[.] \z / $1 / x ;
97+ my $me = uc ( $_ [0] -> { _string } // $_ [0] -> string ) ;
10398
104- return ( uc ( " $self " ) cmp uc ( $other ) );
99+ # Treat undefined value as root
100+ my $other = $_ [1] // q{ } ;
101+
102+ if ( blessed $other and $other -> isa( ' Zonemaster::Engine::DNSName' ) ) {
103+ return $me cmp uc ( $other -> {_string } // $other -> string() );
104+ }
105+ else {
106+ # Assume $other is a string; remove trailing dot except if only character
107+ return $me cmp uc ( $other =~ s / .\K [.] \z // xr );
108+ }
105109}
106110
107111sub next_higher {
@@ -183,7 +187,7 @@ A reference to a list of strings, being the labels the DNS name is made up from.
183187
184188=over
185189
186- =item new($input) _or_ new({ labels => \@labellist})
190+ =item new($input) _or_ new({ labels => \@labellist })
187191
188192The constructor can be called with either a single argument or with a reference
189193to a hash as in the example above.
0 commit comments