File Coverage

Replace.xs
Criterion Covered Total %
statement 59 60 98.3
branch 46 60 76.6
condition n/a
subroutine n/a
pod n/a
total 105 120 87.5


line stmt bran cond sub pod time code
1             /*
2             *
3             * Copyright (c) 2018, cPanel, LLC.
4             * All rights reserved.
5             * http://cpanel.net
6             *
7             * This is free software; you can redistribute it and/or modify it under the
8             * same terms as Perl itself.
9             *
10             */
11              
12             #include
13             #include
14             #include
15             #include
16              
17             #define _REPLACE_BUFFER_SIZE 64
18              
19             #define IS_SPACE(c) c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f'
20              
21             SV *_replace_str( SV *sv, SV *map );
22             SV *_trim_sv( SV *sv );
23              
24 13           SV *_trim_sv( SV *sv ) {
25              
26 13           int len = SvCUR(sv);
27 13           char *str = SvPVX(sv);;
28 13           char *end = str + len - 1;
29              
30             // Skip whitespace at front...
31 32 100         while ( IS_SPACE( (unsigned char) *str) ) {
    100          
    100          
    100          
    100          
32 19           ++str;
33 19           --len;
34             }
35              
36             // Trim at end...
37 32 50         while (end > str && isspace( (unsigned char) *end) ) {
    100          
38 19           *end--;// = 0;
39 19           --len;
40             }
41              
42 13           return newSVpvn_flags( str, len, SvUTF8(sv) );
43             }
44              
45              
46 14           SV *_replace_str( SV *sv, SV *map ) {
47 14           char buffer[_REPLACE_BUFFER_SIZE] = { 0 };
48 14           char *src = SvPVX(sv);
49 14           int len = SvCUR(sv);
50 14           int i = 0;
51 14           char *ptr = src;
52 14           char *str = buffer; /* the new string we are going to use */
53             char *tmp; /* used to grow */
54 14           int str_size = _REPLACE_BUFFER_SIZE; /* we start with the buffer */
55 14           int ix_newstr = 0;
56             AV *mapav;
57             SV *reply;
58              
59 14 50         if ( !map || SvTYPE(map) != SVt_RV || SvTYPE(SvRV(map)) != SVt_PVAV
    100          
    50          
    50          
60 13 0         || AvFILL( SvRV(map) ) <= 0
    100          
61             ) {
62 2           return newSVpv( src, len ); /* no alteration */
63             }
64              
65 12           mapav = (AV *)SvRV(map);
66 12           SV **ary = AvARRAY(mapav);
67              
68              
69 690 100         for ( i = 0; i < len; ++i, ++ptr, ++ix_newstr ) {
70 678           char c = *ptr;
71 678           int ix = (int) ( c );
72 678 100         if ( ix < 0 ) ix = 256 + ix;
73             // need to croak in DEBUG mode if char is invalid
74              
75 678           str[ix_newstr] = c; /* default always performed... */
76 678 50         if ( ix >= AvFILL(mapav)
    100          
77 677 50         || !AvARRAY(mapav)[ix]
78             ) {
79 1           continue;
80             } else {
81 677           SV *entry = AvARRAY(mapav)[ix];
82 677 50         if ( SvPOK( entry ) ) {
83 677           int slen = SvCUR( entry ); /* length of the string used for replacement */
84 677 50         if ( slen <= 0 ) {
85 0           continue;
86             } else {
87 677           char *replace = SvPVX( entry );
88             int j;
89              
90             /* Check if we need to expand. */
91 677 100         if (str_size <= (ix_newstr + slen + 1) ) { /* +1 for \0 */
92             //printf( "#### need to grow %d -> %d\n", str_size, ix_newstr + slen );
93 7           str_size *= 2;
94              
95 7 100         if ( str == buffer ) {
96             /* our first malloc */
97 4 50         Newx(str, str_size, char*);
98 4           memcpy( str, buffer, _REPLACE_BUFFER_SIZE ); /* strncpy stops after the first \0 */
99             } else {
100             /* grow the string */
101 3           tmp = Perl_realloc( str, str_size );
102 3 50         if ( !tmp ) Perl_croak(aTHX_ "failed to realloc string" );
103 3           str = tmp;
104             }
105             }
106              
107             /* replace all characters except the last one, which avoids us to do a --ix_newstr after */
108 802 100         for ( j = 0 ; j < slen - 1; ++j ) {
109 125           str[ix_newstr++] = replace[j];
110             }
111              
112             /* handle the last character */
113 677           str[ix_newstr] = replace[j];
114             }
115             } /* end - SvPOK */
116             } /* end - AvFILL || AvARRAY */
117             }
118              
119 12           str[ix_newstr] = '\0'; /* add the final trailing \0 character */
120              
121 12           reply = newSVpvn_flags( str, ix_newstr, SvUTF8(sv) );
122              
123             /* free our tmp buffer if needed */
124 12 100         if ( str != buffer ) free(str);
125              
126 14           return reply;
127             }
128              
129             MODULE = Char__Replace PACKAGE = Char::Replace
130              
131             SV*
132             replace(sv, map)
133             SV *sv;
134             SV *map;
135             CODE:
136 17 50         if ( sv && SvPOK(sv) ) {
    100          
137 14           RETVAL = _replace_str( sv, map );
138             } else {
139 3           RETVAL = &PL_sv_undef;
140             }
141             OUTPUT:
142             RETVAL
143              
144             SV*
145             trim(sv)
146             SV *sv;
147             CODE:
148 16 50         if ( sv && SvPOK(sv) ) {
    100          
149 13           RETVAL = _trim_sv( sv );
150             } else {
151 3           RETVAL = &PL_sv_undef;
152             }
153             OUTPUT:
154             RETVAL